@sanity/workflow-studio-plugin 0.23.0 → 0.25.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,6 +10,18 @@ function _interopDefaultCompat(e) {
10
10
 
11
11
  var pluralize__default = /* @__PURE__ */ _interopDefaultCompat(pluralize);
12
12
 
13
+ const WORKFLOW_SYSTEM_TYPES = [ workflowEngine.WORKFLOW_INSTANCE_TYPE, workflowEngine.WORKFLOW_DEFINITION_TYPE, workflowEngine.GUARD_DOC_TYPE ];
14
+
15
+ function contentDocumentTypes(schema) {
16
+ return schema.getTypeNames().filter(name => schema.get(name)?.type?.name === "document" && !WORKFLOW_SYSTEM_TYPES.includes(name));
17
+ }
18
+
19
+ const SYSTEM_TYPE_PREFIX = "sanity.";
20
+
21
+ function isSystemDocumentType(name) {
22
+ return name.startsWith(SYSTEM_TYPE_PREFIX) || WORKFLOW_SYSTEM_TYPES.includes(name);
23
+ }
24
+
13
25
  function isLocalContentResource(resource, contentResource) {
14
26
  return resource?.type === "dataset" && resource.id === contentResource.id;
15
27
  }
@@ -34,14 +46,14 @@ function assertUniqueWorkflowMappings(mappings) {
34
46
 
35
47
  function discoverWorkflowMappings(args) {
36
48
  assertUniqueWorkflowMappings(args.overrides ?? []);
37
- const discovered = workflowEngine.latestDeployedDefinitions(args.definitions).flatMap(definition => {
49
+ const bindable = [ ...args.schemaContentTypes ].filter(name => !isSystemDocumentType(name)), discovered = workflowEngine.latestDeployedDefinitions(args.definitions).flatMap(definition => {
38
50
  if (!workflowEngine.isStartableDefinition(definition)) return [];
39
51
  const subject = (definition.fields ?? []).find(workflowEngine.isSubjectEntry);
40
52
  if (subject === void 0 || !workflowEngine.isInputSourced(subject)) return [];
41
53
  const source = {
42
54
  fields: [ ...definition.fields ?? [] ]
43
55
  };
44
- return [ ...args.schemaContentTypes ].filter(docType => workflowEngine.acceptsDocumentType(source, docType)).map(docType => ({
56
+ return bindable.filter(docType => workflowEngine.acceptsDocumentType(source, docType)).map(docType => ({
45
57
  docType: docType,
46
58
  definition: definition.name,
47
59
  label: definition.title ?? definition.name
@@ -172,14 +184,11 @@ function rowProblems(args) {
172
184
  mapping: args.mapping,
173
185
  contentResource: args.contentResource,
174
186
  declared: declared
175
- }), missing = workflowEngine.missingRequiredInputs({
176
- entryDefs: declared,
177
- initialFields: seeded
178
- }), consumptionProblems = workflowEngine.initialFieldIssues({
187
+ });
188
+ return [ ...workflowEngine.initialFieldIssues({
179
189
  workflowFields: declared,
180
190
  initialFields: seeded
181
- }).flatMap(issue => seedConsumptionProblem(issue) ?? []);
182
- return [ ...missing.map(m => `its start would not provide the required entry "${m.name}" (${m.type})`), ...consumptionProblems, ...seeded.flatMap(seed => seedReferenceProblems({
191
+ }).flatMap(issue => seedConsumptionProblem(issue) ?? []), ...seeded.flatMap(seed => seedReferenceProblems({
183
192
  seed: seed,
184
193
  declared: declared
185
194
  })), ...seeded.flatMap(seed => seededSchemaProblems({
@@ -189,12 +198,24 @@ function rowProblems(args) {
189
198
  })) ];
190
199
  }
191
200
 
201
+ const NOT_DEPLOYED = "the definition is not deployed — deploy it or fix the mapping row";
202
+
203
+ function namesNoDeployedDefinition(issue) {
204
+ return issue.version === void 0;
205
+ }
206
+
192
207
  function mappingIssues(args) {
193
208
  const latestByName = new Map(workflowEngine.latestDeployedDefinitions(args.definitions).map(d => [ d.name, d ])), issues = /* @__PURE__ */ new Map;
194
209
  for (const mapping of args.mappings) {
195
- const definition = latestByName.get(mapping.definition);
210
+ const identity = {
211
+ docType: mapping.docType,
212
+ definition: mapping.definition
213
+ }, definition = latestByName.get(mapping.definition);
196
214
  if (definition === void 0) {
197
- issues.set(mappingKey(mapping), `Workflow mapping for document type "${mapping.docType}" points at definition "${mapping.definition}", which is not deployed — deploy it or fix the mapping row.`);
215
+ issues.set(mappingKey(mapping), {
216
+ ...identity,
217
+ problems: [ NOT_DEPLOYED ]
218
+ });
198
219
  continue;
199
220
  }
200
221
  const problems = rowProblems({
@@ -203,11 +224,31 @@ function mappingIssues(args) {
203
224
  contentResource: args.contentResource,
204
225
  schemaContentTypes: args.schemaContentTypes
205
226
  });
206
- problems.length > 0 && issues.set(mappingKey(mapping), `Workflow mapping for document type "${mapping.docType}" points at definition "${mapping.definition}" (v${definition.version}), but ${problems.join("; ")}.`);
227
+ problems.length > 0 && issues.set(mappingKey(mapping), {
228
+ ...identity,
229
+ version: definition.version,
230
+ problems: problems
231
+ });
207
232
  }
208
233
  return issues;
209
234
  }
210
235
 
236
+ function sameMappingIssue(left, right) {
237
+ return left.docType === right.docType && left.definition === right.definition && left.version === right.version && left.problems.length === right.problems.length && left.problems.every((problem, index) => problem === right.problems[index]);
238
+ }
239
+
240
+ function mappingIssueDetail(issue) {
241
+ return issue.problems.join("; ");
242
+ }
243
+
244
+ function versionSuffix(issue) {
245
+ return issue.version === void 0 ? "" : ` (v${issue.version})`;
246
+ }
247
+
248
+ function mappingIssueLogLines(issues) {
249
+ return [ ...issues.values() ].map(issue => `The "${issue.docType}" → "${issue.definition}" mapping row${versionSuffix(issue)} is disabled: ${mappingIssueDetail(issue)}.`);
250
+ }
251
+
211
252
  const WORKFLOWS_VIEW_ID = "workflows", WORKFLOWS_TAB_PARAM = "workflowsTab";
212
253
 
213
254
  function withoutWorkflowsTab(params) {
@@ -250,10 +291,16 @@ function landingDefaultOpen(landing, instanceId) {
250
291
  if (!(landing === void 0 || !landing.instanceIds.includes(instanceId))) return instanceId === landing.focusedId;
251
292
  }
252
293
 
253
- const WORKFLOWS_TOOL_NAME = "workflows", WORKFLOW_INTENT = "workflow";
294
+ const WORKFLOWS_TOOL_NAME = "workflows";
295
+
296
+ function workflowsToolAvailable(tools) {
297
+ return tools.some(tool => tool.name === WORKFLOWS_TOOL_NAME);
298
+ }
299
+
300
+ const WORKFLOW_INTENT = "workflow";
254
301
 
255
- function pathSegmentTab(args) {
256
- return args.tabs.find(tab => tab === args.segment) ?? args.fallback;
302
+ function pathSegmentName(args) {
303
+ return args.names.find(name => name === args.segment);
257
304
  }
258
305
 
259
306
  function handlesWorkflowIntent(intent, params) {
@@ -267,11 +314,23 @@ function workflowIntentState(intent, params) {
267
314
  } : null;
268
315
  }
269
316
 
270
- const pad2 = n => String(n).padStart(2, "0");
317
+ 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);
318
+
319
+ function isDateFieldKind(kind) {
320
+ return DATE_FIELD_KIND_SET.has(kind);
321
+ }
322
+
323
+ function isDueDateFieldKind(kind) {
324
+ return DUE_DATE_FIELD_KIND_SET.has(kind);
325
+ }
326
+
327
+ function hasTimeOfDay(kind) {
328
+ return kind === "datetime" || kind === "dueDatetime";
329
+ }
271
330
 
272
331
  function parseDateFieldValue(value, kind) {
273
332
  if (typeof value != "string" || value === "") return;
274
- const parsed2 = kind === "date" ? parseLocalDate(value) : new Date(value);
333
+ const parsed2 = hasTimeOfDay(kind) ? new Date(value) : parseLocalDate(value);
275
334
  return Number.isNaN(parsed2.getTime()) ? void 0 : parsed2;
276
335
  }
277
336
 
@@ -287,16 +346,24 @@ function parseStoredDateValue(value) {
287
346
  }
288
347
 
289
348
  function serializeDateFieldValue(date, kind) {
290
- return kind === "datetime" ? date.toISOString() : `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
349
+ return hasTimeOfDay(kind) ? date.toISOString() : `${date.getFullYear()}-${pad2(date.getMonth() + 1)}-${pad2(date.getDate())}`;
350
+ }
351
+
352
+ class EffectsIncompleteError extends Error {
353
+ reason;
354
+ constructor(reason, options) {
355
+ super(`The change was committed, but running its effects failed: ${reason}`, options),
356
+ this.name = "EffectsIncompleteError", this.reason = reason;
357
+ }
291
358
  }
292
359
 
293
360
  const editDisabledSentence = {
294
361
  "edit-window-closed": () => "Can only be edited while its activity is active",
295
- "editor-not-permitted": () => "You don't have permission to edit this",
362
+ "editor-not-permitted": () => "You dont have permission to edit this",
296
363
  "instance-aborted": () => "This workflow was aborted",
297
364
  "instance-completed": () => "This workflow is already complete",
298
365
  "mutation-guard-denied": () => "An earlier step is holding this document",
299
- "not-editable": () => "This isn't editable here"
366
+ "not-editable": () => "This isnt editable here"
300
367
  };
301
368
 
302
369
  function describeEditDisabledReason(reason) {
@@ -369,24 +436,43 @@ const activityNotActiveSentence = {
369
436
  }, disabledSentence = {
370
437
  "activity-not-active": r => activityNotActiveSentence[r.status],
371
438
  "cascade-fired": () => "This happens automatically — the workflow runs it when its condition is met.",
372
- "filter-failed": () => "Not available to you yet — you may not have the required role, it may be assigned to someone else, or a needed value isn't set.",
439
+ "filter-failed": () => "Not available to you yet — you may not have the required role, it may be assigned to someone else, or a needed value isnt set.",
373
440
  "instance-aborted": () => "This workflow was aborted.",
374
441
  "instance-completed": () => "This workflow is already complete.",
375
442
  "mutation-guard-denied": () => "An earlier step is holding this document.",
376
- "requirements-unmet": () => "This step isn't ready yet — a required condition hasn't been met.",
443
+ "requirements-unmet": () => "This step isnt ready yet — a required condition hasnt been met.",
377
444
  "stage-terminal": () => "This stage is finished.",
378
- "subject-permission-denied": () => "You don't have permission to change the document this step works on."
445
+ "subject-permission-denied": () => "You dont have permission to change the document this step works on."
379
446
  };
380
447
 
381
448
  function describeDisabledReason(reason) {
382
449
  return disabledSentence[reason.kind](reason);
383
450
  }
384
451
 
452
+ function readRejection(err) {
453
+ return err instanceof workflowEngine.ActionDisabledError ? {
454
+ kind: "refusal",
455
+ message: describeDisabledReason(err.reason)
456
+ } : err instanceof workflowEngine.MutationGuardDeniedError ? {
457
+ kind: "refusal",
458
+ message: describeDisabledReason({
459
+ kind: "mutation-guard-denied",
460
+ denied: err.denied
461
+ })
462
+ } : err instanceof workflowEngine.EditFieldDeniedError ? {
463
+ kind: "refusal",
464
+ message: describeEditDisabledReason(err.reason)
465
+ } : err instanceof EffectsIncompleteError ? {
466
+ kind: "committed-incomplete",
467
+ message: err.reason
468
+ } : {
469
+ kind: "failure",
470
+ message: workflowEngine.errorMessage(err)
471
+ };
472
+ }
473
+
385
474
  function describeError(err) {
386
- return err instanceof workflowEngine.ActionDisabledError ? describeDisabledReason(err.reason) : err instanceof workflowEngine.MutationGuardDeniedError ? describeDisabledReason({
387
- kind: "mutation-guard-denied",
388
- denied: err.denied
389
- }) : workflowEngine.errorMessage(err);
475
+ return readRejection(err).message;
390
476
  }
391
477
 
392
478
  function hrefIfHttp(value) {
@@ -442,6 +528,28 @@ function isTodoOverdue(item, today = /* @__PURE__ */ new Date) {
442
528
  return !item.dueDate || isTodoDone(item) ? !1 : item.dueDate < serializeDateFieldValue(today, "date");
443
529
  }
444
530
 
531
+ function todoEditKind(patch) {
532
+ return "assignee" in patch ? "assignee" : "due-date";
533
+ }
534
+
535
+ function assertNamesOneRow(items, key) {
536
+ const matches = items.filter(row => row._key === key).length;
537
+ if (matches !== 1) throw new Error(matches === 0 ? "This to-do is no longer in the stored list. Reload and try again." : `${matches} to-dos in this list share one id, so they cannot be told apart. The stored list has to be repaired before it can be edited.`);
538
+ }
539
+
540
+ function todoItemsPatched(args) {
541
+ const {items: items, key: key, patch: patch} = args;
542
+ return assertNamesOneRow(items, key), items.map(row => row._key === key ? {
543
+ ...row,
544
+ ...patch
545
+ } : row);
546
+ }
547
+
548
+ function todoItemsWithout(args) {
549
+ const {items: items, key: key} = args;
550
+ return assertNamesOneRow(items, key), items.filter(row => row._key !== key);
551
+ }
552
+
445
553
  function tickOpsOf(action) {
446
554
  return (action.ops ?? []).flatMap(o => o.type === "field.updateWhere" ? [ o ] : []);
447
555
  }
@@ -882,11 +990,23 @@ function rowAssignControlState(args) {
882
990
 
883
991
  function rowDateControlState(args) {
884
992
  return rowControlState({
885
- kinds: [ "date", "datetime" ],
993
+ kinds: DUE_DATE_FIELD_KINDS,
886
994
  ...args
887
995
  });
888
996
  }
889
997
 
998
+ function dateValuesOf(state, matches) {
999
+ return state.filter(entry => matches(entry._type)).map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
1000
+ }
1001
+
1002
+ function datesOf(state) {
1003
+ return dateValuesOf(state, isDateFieldKind);
1004
+ }
1005
+
1006
+ function dueDatesOf(state) {
1007
+ return dateValuesOf(state, isDueDateFieldKind);
1008
+ }
1009
+
890
1010
  function committedRowFace(row) {
891
1011
  return {
892
1012
  activityName: row.activityName,
@@ -898,7 +1018,11 @@ function committedRowFace(row) {
898
1018
  }
899
1019
 
900
1020
  function activityRowProps(args) {
901
- const {activityEval: activityEval, activityState: activityState, editableFields: editableFields} = args, state = activityState.get(activityEval.activity.name) ?? [];
1021
+ const {activityEval: activityEval, activityState: activityState, editableFields: editableFields} = args, state = activityState.get(activityEval.activity.name) ?? [], controlArgs = {
1022
+ activity: activityEval.activity,
1023
+ status: activityEval.status,
1024
+ editableFields: editableFields
1025
+ };
902
1026
  return {
903
1027
  face: {
904
1028
  activityName: activityEval.activity.name,
@@ -907,12 +1031,10 @@ function activityRowProps(args) {
907
1031
  automated: showsAutomatedPill(activityEval),
908
1032
  blocked: showsBlockedTag(activityEval)
909
1033
  },
910
- assignState: rowAssignControlState({
911
- activity: activityEval.activity,
912
- status: activityEval.status,
913
- editableFields: editableFields
914
- }),
1034
+ assignState: rowAssignControlState(controlArgs),
915
1035
  assigneeIds: assigneeUserIdsOf(state),
1036
+ dateState: rowDateControlState(controlArgs),
1037
+ dueDates: dueDatesOf(state),
916
1038
  terminalActions: rowTerminalActions({
917
1039
  activityEval: activityEval,
918
1040
  state: state,
@@ -922,12 +1044,12 @@ function activityRowProps(args) {
922
1044
  }
923
1045
 
924
1046
  function dateControlKind(state) {
925
- if (state.kind === "editable" && (state.field.type === "date" || state.field.type === "datetime")) return state.field.type;
1047
+ if (state.kind === "editable" && isDueDateFieldKind(state.field.type)) return state.field.type;
926
1048
  }
927
1049
 
928
1050
  function dateControlValue(args) {
929
- const {state: state, dates: dates} = args;
930
- return state.kind === "editable" ? typeof state.field.value == "string" && state.field.value !== "" ? state.field.value : void 0 : dates.find(value => parseStoredDateValue(value) !== void 0);
1051
+ const {state: state, dueDates: dueDates} = args;
1052
+ return state.kind === "editable" ? typeof state.field.value == "string" && state.field.value !== "" ? state.field.value : void 0 : dueDates.find(value => parseStoredDateValue(value) !== void 0);
931
1053
  }
932
1054
 
933
1055
  function activityStateOf(instance, activityName) {
@@ -952,7 +1074,7 @@ function deriveActivityDetail(args) {
952
1074
  };
953
1075
  }
954
1076
 
955
- const WORKFLOW_API_VERSION = workflowEngine.ENGINE_API_VERSION, WORKFLOW_SYSTEM_TYPES = [ workflowEngine.WORKFLOW_INSTANCE_TYPE, workflowEngine.WORKFLOW_DEFINITION_TYPE, workflowEngine.GUARD_DOC_TYPE ], EMPTY_ENTRIES = [], WorkflowContext = react.createContext(null);
1077
+ const WORKFLOW_API_VERSION = workflowEngine.ENGINE_API_VERSION, EMPTY_ENTRIES = [], WorkflowContext = react.createContext(null);
956
1078
 
957
1079
  function useWorkflowContext() {
958
1080
  const value = react.useContext(WorkflowContext);
@@ -994,7 +1116,7 @@ function useWorkflowInstanceEntry(instanceId, options = {}) {
994
1116
 
995
1117
  const WorkflowToolOpened = telemetry.defineEvent({
996
1118
  name: "Editorial Workflows Studio Plugin Tool Opened",
997
- version: 1,
1119
+ version: 2,
998
1120
  description: "The Workflows tool was opened or its tab was switched"
999
1121
  }), WorkflowDocumentViewOpened = telemetry.defineEvent({
1000
1122
  name: "Editorial Workflows Studio Plugin Document View Opened",
@@ -1004,10 +1126,27 @@ const WorkflowToolOpened = telemetry.defineEvent({
1004
1126
  name: "Editorial Workflows Studio Plugin Instance Detail Viewed",
1005
1127
  version: 1,
1006
1128
  description: "The Workflows tool's per-instance detail view was opened"
1129
+ });
1130
+
1131
+ function definitionFingerprint(definition) {
1132
+ const hash = definition?.contentHash;
1133
+ return hash === void 0 ? {} : {
1134
+ definitionContentHash: hash
1135
+ };
1136
+ }
1137
+
1138
+ const WorkflowDefinitionDetailViewed = telemetry.defineEvent({
1139
+ name: "Editorial Workflows Studio Plugin Definition Detail Viewed",
1140
+ version: 1,
1141
+ description: "A workflow's definition page was opened in the Workflows tool"
1007
1142
  }), WorkflowFormStripClicked = telemetry.defineEvent({
1008
1143
  name: "Editorial Workflows Studio Plugin Form Strip Clicked",
1009
1144
  version: 1,
1010
1145
  description: "The form strip's instance line was clicked, opening the document view"
1146
+ }), WorkflowDocumentLinkClicked = telemetry.defineEvent({
1147
+ name: "Editorial Workflows Studio Plugin Document Link Clicked",
1148
+ version: 2,
1149
+ description: "A document link was clicked, opening the referenced document's editor"
1011
1150
  }), WorkflowStartDialogOpened = telemetry.defineEvent({
1012
1151
  name: "Editorial Workflows Studio Plugin Start Dialog Opened",
1013
1152
  version: 1,
@@ -1022,8 +1161,12 @@ const WorkflowToolOpened = telemetry.defineEvent({
1022
1161
  description: "A fresh document's auto-start ran — one start request per configured workflow"
1023
1162
  }), WorkflowActionControlUsed = telemetry.defineEvent({
1024
1163
  name: "Editorial Workflows Studio Plugin Action Control Used",
1025
- version: 1,
1164
+ version: 2,
1026
1165
  description: "An action-firing control was used, attributed to its UI surface"
1166
+ }), WorkflowFieldControlUsed = telemetry.defineEvent({
1167
+ name: "Editorial Workflows Studio Plugin Field Control Used",
1168
+ version: 1,
1169
+ description: "A field-editing control committed an edit, attributed to its UI surface"
1027
1170
  }), WorkflowActivityDialogOpened = telemetry.defineEvent({
1028
1171
  name: "Editorial Workflows Studio Plugin Activity Dialog Opened",
1029
1172
  version: 1,
@@ -1032,10 +1175,30 @@ const WorkflowToolOpened = telemetry.defineEvent({
1032
1175
  name: "Editorial Workflows Studio Plugin Task Filters Applied",
1033
1176
  version: 1,
1034
1177
  description: "The tool's task-filter menu closed with a changed selection"
1178
+ }), WorkflowBoardWorkflowSelected = telemetry.defineEvent({
1179
+ name: "Editorial Workflows Studio Plugin Board Workflow Selected",
1180
+ version: 2,
1181
+ description: "The workflow documents page settled on the workflow it's showing"
1182
+ }), WorkflowTitleSeedDrifted = telemetry.defineEvent({
1183
+ name: "Editorial Workflows Studio Plugin Title Seed Drifted",
1184
+ version: 1,
1185
+ description: "A cold-start seeded preview title diverged from the live preview pipeline"
1035
1186
  }), WorkflowTodoToggled = telemetry.defineEvent({
1036
1187
  name: "Editorial Workflows Studio Plugin Todo Toggled",
1037
1188
  version: 1,
1038
1189
  description: "A todo checkbox was toggled, attributed to its UI surface and write seam"
1190
+ }), WorkflowTodoEdited = telemetry.defineEvent({
1191
+ name: "Editorial Workflows Studio Plugin Todo Edited",
1192
+ version: 1,
1193
+ description: "A non-toggle todo-list write, attributed to its UI surface and gesture"
1194
+ }), WorkflowAbortDialogOpened = telemetry.defineEvent({
1195
+ name: "Editorial Workflows Studio Plugin Abort Dialog Opened",
1196
+ version: 1,
1197
+ description: "The abort-workflow confirm was opened"
1198
+ }), WorkflowAbortDialogSubmitted = telemetry.defineEvent({
1199
+ name: "Editorial Workflows Studio Plugin Abort Dialog Submitted",
1200
+ version: 1,
1201
+ description: "The abort-workflow confirm was submitted, carrying the attempt's outcome"
1039
1202
  });
1040
1203
 
1041
1204
  function useLogEventOnMount(event, data) {
@@ -1125,20 +1288,32 @@ function useUserDisplay(id) {
1125
1288
  };
1126
1289
  }
1127
1290
 
1128
- const warnedNotMember = /* @__PURE__ */ new Set;
1291
+ const warnedSelfUnavailable = /* @__PURE__ */ new Set;
1292
+
1293
+ function warnSelfUnavailableOnce(key, message) {
1294
+ warnedSelfUnavailable.has(key) || (warnedSelfUnavailable.add(key), console.warn(message));
1295
+ }
1296
+
1297
+ function bridgedSelfId(users, meId) {
1298
+ const classified = workflowEngine.classifyPrincipalId(meId);
1299
+ return users.find(user => user.membership.id === meId)?.profile?.sanityUserId ?? classified.globalId ?? (classified.namespace === "project" ? void 0 : meId);
1300
+ }
1129
1301
 
1130
1302
  function useBridgedSelf() {
1131
- const me = sanity.useCurrentUser(), {users: users, loading: loading} = workflowStudio.useStudioProjectUsers();
1132
- if (!me || loading) return;
1133
- const classified = workflowEngine.classifyPrincipalId(me.id), id = users.find(user => user.membership.id === me.id)?.profile?.sanityUserId ?? classified.globalId ?? (classified.namespace === "project" ? void 0 : me.id);
1134
- if (id === void 0) {
1135
- warnedNotMember.has(me.id) || (warnedNotMember.add(me.id), console.warn(`workflow: the project member directory cannot resolve the logged-in user ("${me.id}") to an account-global identity — the engine stores account-global user ids only, so workflow assignment and authoring controls stay disabled. Typically this user is not a member of this project.`));
1136
- return;
1137
- }
1138
- return {
1303
+ const me = sanity.useCurrentUser(), {users: users, loading: loading, error: error} = workflowStudio.useStudioProjectUsers();
1304
+ if (!me) return;
1305
+ const id = bridgedSelfId(users, me.id);
1306
+ if (id !== void 0) return {
1139
1307
  id: id,
1140
1308
  roles: me.roles ?? []
1141
1309
  };
1310
+ if (!loading) {
1311
+ if (error !== void 0) {
1312
+ warnSelfUnavailableOnce(`load:${me.id}`, `workflow: the project member directory failed to load ("${workflowEngine.errorMessage(error)}") — the logged-in user ("${me.id}") is project-scoped, and only that directory maps it to the account-global identity the engine stores, so workflow assignment and authoring controls stay disabled. A mounted view does not retry on its own; the next consumer to ask reloads the directory once its backoff has passed.`);
1313
+ return;
1314
+ }
1315
+ warnSelfUnavailableOnce(`unresolved:${me.id}`, `workflow: the project member directory cannot resolve the logged-in user ("${me.id}") to an account-global identity — the engine stores account-global user ids only, so workflow assignment and authoring controls stay disabled. Typically this user is not a member of this project.`);
1316
+ }
1142
1317
  }
1143
1318
 
1144
1319
  function useSelfActor() {
@@ -1258,9 +1433,50 @@ function buildParams(decls, values) {
1258
1433
  };
1259
1434
  }
1260
1435
 
1261
- const NEVER_MS = 2 ** 31 - 1;
1436
+ function useDelayedFlag(active, delayMs) {
1437
+ const [held, setHeld] = react.useState(!1);
1438
+ return react.useEffect(() => {
1439
+ if (!active) {
1440
+ setHeld(!1);
1441
+ return;
1442
+ }
1443
+ const timer = setTimeout(() => setHeld(!0), delayMs);
1444
+ return () => clearTimeout(timer);
1445
+ }, [ active, delayMs ]), held && active;
1446
+ }
1447
+
1448
+ const NEVER_MS = 2 ** 31 - 1, OPEN_DELAY_MS = 300;
1262
1449
 
1263
1450
  function HoverHint(props) {
1451
+ return props.anchor !== void 0 ? /* @__PURE__ */ jsxRuntime.jsx(AnchoredHint, {
1452
+ ...props
1453
+ }) : /* @__PURE__ */ jsxRuntime.jsx(WrappingHint, {
1454
+ ...props
1455
+ });
1456
+ }
1457
+
1458
+ function AnchoredHint(props) {
1459
+ const {disabled: disabled = !1} = props, open = useDelayedFlag(props.open === !0 && !disabled, OPEN_DELAY_MS);
1460
+ /* @__PURE__ */
1461
+ return jsxRuntime.jsx(ui.Popover, {
1462
+ animate: !0,
1463
+ content: /* @__PURE__ */ jsxRuntime.jsx(HintBody, {
1464
+ ...props
1465
+ }),
1466
+ open: open,
1467
+ padding: hintPadding(props),
1468
+ placement: props.placement ?? "top",
1469
+ portal: !0,
1470
+ radius: 3,
1471
+ referenceElement: props.anchor
1472
+ });
1473
+ }
1474
+
1475
+ function hintPadding(props) {
1476
+ return props.content !== void 0 && props.flush ? 0 : 2;
1477
+ }
1478
+
1479
+ function WrappingHint(props) {
1264
1480
  const {children: children, disabled: disabled = !1} = props;
1265
1481
  /* @__PURE__ */
1266
1482
  return jsxRuntime.jsx(ui.Tooltip, {
@@ -1270,17 +1486,20 @@ function HoverHint(props) {
1270
1486
  }),
1271
1487
  delay: {
1272
1488
  close: 0,
1273
- open: disabled ? NEVER_MS : 300
1489
+ open: disabled ? NEVER_MS : OPEN_DELAY_MS
1274
1490
  },
1275
- padding: props.content !== void 0 && props.flush ? 0 : 2,
1276
- placement: "top",
1491
+ padding: hintPadding(props),
1492
+ placement: props.placement ?? "top",
1277
1493
  portal: !0,
1278
1494
  radius: 3,
1279
1495
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
1280
1496
  "data-testid": props["data-testid"],
1281
1497
  style: {
1282
- display: "inline-flex",
1283
- maxWidth: "100%"
1498
+ display: props.fill ? "flex" : "inline-flex",
1499
+ maxWidth: "100%",
1500
+ ...props.fill ? {
1501
+ width: "100%"
1502
+ } : {}
1284
1503
  },
1285
1504
  children: children
1286
1505
  })
@@ -1368,7 +1587,7 @@ function DismissablePopover({children: children, content: content2, onDismiss: o
1368
1587
  function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly: readOnly = !1}) {
1369
1588
  const [open, setOpen] = react.useState(!1), parsed2 = parseDateFieldValue(value, kind), close = () => setOpen(!1), toggle = () => {
1370
1589
  readOnly || setOpen(v => !v);
1371
- };
1590
+ }, pickHint = hasTimeOfDay(kind) ? "Pick a date and time…" : "Pick a date…";
1372
1591
  /* @__PURE__ */
1373
1592
  return jsxRuntime.jsx(DismissablePopover, {
1374
1593
  content: /* @__PURE__ */ jsxRuntime.jsx(workflowComponents.ClearableDatePicker, {
@@ -1379,7 +1598,7 @@ function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly:
1379
1598
  onPick: next => {
1380
1599
  close(), onChange(serializeDateFieldValue(next, kind));
1381
1600
  },
1382
- selectTime: kind === "datetime",
1601
+ selectTime: hasTimeOfDay(kind),
1383
1602
  value: parsed2
1384
1603
  }),
1385
1604
  onDismiss: close,
@@ -1392,7 +1611,7 @@ function DateFieldInput({kind: kind, value: value, onChange: onChange, readOnly:
1392
1611
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, {
1393
1612
  fontSize: 1,
1394
1613
  onClick: toggle,
1395
- placeholder: readOnly ? "" : kind === "datetime" ? "Pick a date and time…" : "Pick a date…",
1614
+ placeholder: readOnly ? "" : pickHint,
1396
1615
  readOnly: !0,
1397
1616
  value: parsed2 ? formatDate(String(value)) : ""
1398
1617
  })
@@ -1436,7 +1655,7 @@ function ChoiceSelect({options: options, value: value, onChange: onChange, readO
1436
1655
  });
1437
1656
  }
1438
1657
 
1439
- const SCALAR_INPUT_KINDS = [ "string", "text", "url", "number", "progress", "date", "dateTime", "datetime", "boolean" ], SCALAR_INPUT_KIND_SET = new Set(SCALAR_INPUT_KINDS);
1658
+ const SCALAR_INPUT_KINDS = [ "string", "text", "url", "number", "progress", ...DATE_FIELD_KINDS, "dateTime", "boolean" ], SCALAR_INPUT_KIND_SET = new Set(SCALAR_INPUT_KINDS);
1440
1659
 
1441
1660
  function isScalarInputKind(kind) {
1442
1661
  return SCALAR_INPUT_KIND_SET.has(kind);
@@ -1454,8 +1673,8 @@ function ScalarInput({kind: kind, value: value, onChange: onChange, options: opt
1454
1673
  }) : kind === "boolean" ? /* @__PURE__ */ jsxRuntime.jsx(ui.Checkbox, {
1455
1674
  checked: value === !0,
1456
1675
  onChange: e => onChange(e.currentTarget.checked)
1457
- }) : kind === "date" || kind === "dateTime" || kind === "datetime" ? /* @__PURE__ */ jsxRuntime.jsx(DateFieldInput, {
1458
- kind: kind === "date" ? "date" : "datetime",
1676
+ }) : isDateFieldKind(kind) || kind === "dateTime" ? /* @__PURE__ */ jsxRuntime.jsx(DateFieldInput, {
1677
+ kind: kind === "dateTime" ? "datetime" : kind,
1459
1678
  onChange: next => onChange(next ?? ""),
1460
1679
  value: value
1461
1680
  }) : /* @__PURE__ */ jsxRuntime.jsx(ui.TextInput, {
@@ -1561,11 +1780,16 @@ function useStubValuePreview(id, schemaType) {
1561
1780
  };
1562
1781
  }
1563
1782
 
1783
+ function PreviewPlaceholder() {
1784
+ /* @__PURE__ */
1785
+ return jsxRuntime.jsx(sanity.SanityDefaultPreview, {
1786
+ isPlaceholder: !0
1787
+ });
1788
+ }
1789
+
1564
1790
  function StubPreview({id: id, schemaType: schemaType}) {
1565
1791
  const {value: value, preview: preview} = useStubValuePreview(id, schemaType);
1566
- return preview.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(sanity.SanityDefaultPreview, {
1567
- isPlaceholder: !0
1568
- }) : /* @__PURE__ */ jsxRuntime.jsx(sanity.Preview, {
1792
+ return preview.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(PreviewPlaceholder, {}) : /* @__PURE__ */ jsxRuntime.jsx(sanity.Preview, {
1569
1793
  layout: "default",
1570
1794
  schemaType: schemaType,
1571
1795
  skipVisibilityCheck: !0,
@@ -1798,14 +2022,87 @@ function ParamsForm({decls: decls, values: values, onChange: onChange}) {
1798
2022
  });
1799
2023
  }
1800
2024
 
1801
- function useClosableToast() {
2025
+ const TOAST_ID = {
2026
+ abort: "workflow-abort",
2027
+ detailsCopy: "workflow-details-copy",
2028
+ documentCreate: "workflow-document-create",
2029
+ documentSave: "workflow-document-save",
2030
+ effectResolve: "workflow-effect-resolve",
2031
+ effectsDrain: "workflow-effects-drain",
2032
+ effectsIncomplete: "workflow-effects-incomplete",
2033
+ orphanSettle: "workflow-orphan-settle",
2034
+ start: "workflow-start"
2035
+ };
2036
+
2037
+ function instanceToastId(gesture, instanceId) {
2038
+ return `workflow-${gesture}:${instanceId}`;
2039
+ }
2040
+
2041
+ function actionFireToastId(args) {
2042
+ return `workflow-action-fire:${args.instanceId}:${args.activity}:${args.action}`;
2043
+ }
2044
+
2045
+ const REASON_TOAST_DURATION = 2e4, DISMISS_SENTINEL_DURATION = .01;
2046
+
2047
+ function carriesReason(params) {
2048
+ return params.description !== void 0 && params.status !== "info";
2049
+ }
2050
+
2051
+ function withConvention(params) {
2052
+ return {
2053
+ ...params,
2054
+ closable: !0,
2055
+ ...carriesReason(params) ? {
2056
+ duration: REASON_TOAST_DURATION
2057
+ } : {}
2058
+ };
2059
+ }
2060
+
2061
+ function useWorkflowToast() {
1802
2062
  const toast = ui.useToast();
1803
2063
  return react.useMemo(() => ({
1804
- ...toast,
1805
- push: params => toast.push({
1806
- ...params,
1807
- closable: !0
1808
- })
2064
+ push: params => {
2065
+ toast.push(withConvention(params));
2066
+ },
2067
+ dismiss: id => {
2068
+ toast.push({
2069
+ id: id,
2070
+ duration: DISMISS_SENTINEL_DURATION
2071
+ });
2072
+ }
2073
+ }), [ toast ]);
2074
+ }
2075
+
2076
+ const COMMITTED_INCOMPLETE_TITLE = "Saved, but its follow-up didn’t run";
2077
+
2078
+ function rejectionToast(reading, titles) {
2079
+ return reading.kind === "committed-incomplete" ? {
2080
+ id: TOAST_ID.effectsIncomplete,
2081
+ status: "warning",
2082
+ title: COMMITTED_INCOMPLETE_TITLE,
2083
+ description: reading.message
2084
+ } : reading.kind === "refusal" ? {
2085
+ id: titles.id,
2086
+ status: "warning",
2087
+ title: titles.refused,
2088
+ description: reading.message
2089
+ } : {
2090
+ id: titles.id,
2091
+ status: "error",
2092
+ title: titles.failed,
2093
+ description: reading.message
2094
+ };
2095
+ }
2096
+
2097
+ function useRejectionReport() {
2098
+ const toast = useWorkflowToast();
2099
+ return react.useMemo(() => ({
2100
+ rejected: args => {
2101
+ const reading = readRejection(args.err);
2102
+ reading.kind !== "refusal" && console.error(`[workflow-studio-plugin] ${args.context} rejected:`, args.err),
2103
+ toast.push(rejectionToast(reading, args));
2104
+ },
2105
+ dismiss: toast.dismiss
1809
2106
  }), [ toast ]);
1810
2107
  }
1811
2108
 
@@ -1828,10 +2125,21 @@ function useActionClusterPending() {
1828
2125
  }
1829
2126
 
1830
2127
  function useFireAction(args) {
1831
- const {instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, viaMenu: viaMenu} = args, {fireActionFor: fireActionFor} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), toast = useClosableToast(), cluster = useActionClusterLock(), [localPending, setLocalPending] = react.useState(!1), pending = localPending || cluster?.pending === !0;
2128
+ const {instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, placement: placement, viaMenu: viaMenu} = args, {fireActionFor: fireActionFor} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), report = useRejectionReport(), cluster = useActionClusterLock(), [localPending, setLocalPending] = react.useState(!1), pending = localPending || cluster?.pending === !0, toastId = actionFireToastId({
2129
+ instanceId: instanceId,
2130
+ activity: activity,
2131
+ action: action
2132
+ });
1832
2133
  return {
1833
2134
  fire: react.useCallback(async params => {
1834
2135
  if (pending) return !1;
2136
+ const log = success => telemetry2.log(WorkflowActionControlUsed, {
2137
+ instanceId: instanceId,
2138
+ surface: surface,
2139
+ placement: placement,
2140
+ viaMenu: viaMenu,
2141
+ success: success
2142
+ });
1835
2143
  setLocalPending(!0), cluster?.onFireChange(!0);
1836
2144
  try {
1837
2145
  return await fireActionFor(instanceId, {
@@ -1840,31 +2148,19 @@ function useFireAction(args) {
1840
2148
  ...params && Object.keys(params).length > 0 ? {
1841
2149
  params: params
1842
2150
  } : {}
1843
- }), toast.push({
1844
- status: "success",
1845
- title: `${label} done`
1846
- }), telemetry2.log(WorkflowActionControlUsed, {
1847
- instanceId: instanceId,
1848
- surface: surface,
1849
- viaMenu: viaMenu,
1850
- success: !0
1851
- }), !0;
2151
+ }), report.dismiss(toastId), log(!0), !0;
1852
2152
  } catch (err) {
1853
- return console.error(`[workflow-studio-plugin] firing "${action}" on "${activity}" failed:`, err),
1854
- toast.push({
1855
- status: "error",
1856
- title: `${label} failed`,
1857
- description: describeError(err)
1858
- }), telemetry2.log(WorkflowActionControlUsed, {
1859
- instanceId: instanceId,
1860
- surface: surface,
1861
- viaMenu: viaMenu,
1862
- success: !1
1863
- }), !1;
2153
+ return report.rejected({
2154
+ err: err,
2155
+ context: `firing "${action}" on "${activity}"`,
2156
+ id: toastId,
2157
+ refused: `“${label}” isn’t available right now`,
2158
+ failed: `Failed to complete “${label}”`
2159
+ }), log(!1), !1;
1864
2160
  } finally {
1865
2161
  setLocalPending(!1), cluster?.onFireChange(!1);
1866
2162
  }
1867
- }, [ fireActionFor, instanceId, activity, action, label, pending, toast, cluster, telemetry2, surface, viaMenu ]),
2163
+ }, [ fireActionFor, instanceId, activity, action, label, pending, report, toastId, cluster, telemetry2, surface, placement, viaMenu ]),
1868
2164
  pending: pending
1869
2165
  };
1870
2166
  }
@@ -1905,13 +2201,14 @@ function RowClickShield({active: active, children: children}) {
1905
2201
  });
1906
2202
  }
1907
2203
 
1908
- function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
2204
+ function FireButton({instanceId: instanceId, activity: activity, action: action, label: label, surface: surface, placement: placement, mode: mode = "default", chrome: chrome, tone: tone = "default", icon: icon}) {
1909
2205
  const {fire: fire, pending: pending} = useFireAction({
1910
2206
  instanceId: instanceId,
1911
2207
  activity: activity,
1912
2208
  action: action,
1913
2209
  label: label,
1914
2210
  surface: surface,
2211
+ placement: placement,
1915
2212
  viaMenu: !1
1916
2213
  }), handleClick = e => {
1917
2214
  chrome?.inButtonCard === !0 && e.stopPropagation(), !pending && fire();
@@ -1933,7 +2230,7 @@ function FireButton({instanceId: instanceId, activity: activity, action: action,
1933
2230
  });
1934
2231
  }
1935
2232
 
1936
- function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2233
+ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
1937
2234
  const rowKind = actionRowKind(actionEval);
1938
2235
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsxRuntime.jsx(DisabledActionButton, {
1939
2236
  actionEval: actionEval,
@@ -1944,12 +2241,14 @@ function ActivityActionRow({actionEval: actionEval, instanceId: instanceId, acti
1944
2241
  instanceId: instanceId,
1945
2242
  activity: activity,
1946
2243
  chrome: chrome,
2244
+ placement: placement,
1947
2245
  surface: surface
1948
2246
  }) : /* @__PURE__ */ jsxRuntime.jsx(ParamsActionButton, {
1949
2247
  actionEval: actionEval,
1950
2248
  instanceId: instanceId,
1951
2249
  activity: activity,
1952
2250
  chrome: chrome,
2251
+ placement: placement,
1953
2252
  surface: surface
1954
2253
  });
1955
2254
  }
@@ -1975,7 +2274,7 @@ function DisabledActionButton({actionEval: actionEval, activity: activity, chrom
1975
2274
  }) : button;
1976
2275
  }
1977
2276
 
1978
- function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2277
+ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
1979
2278
  const {action: action} = actionEval, label = actionLabel(action), btn = actionButtonFace({
1980
2279
  actionEval: actionEval,
1981
2280
  activityName: activity
@@ -1987,6 +2286,7 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
1987
2286
  mode: btn.mode,
1988
2287
  activity: activity,
1989
2288
  chrome: chrome,
2289
+ placement: placement,
1990
2290
  surface: surface,
1991
2291
  tone: btn.tone
1992
2292
  });
@@ -1996,13 +2296,14 @@ function PlainActionButton({actionEval: actionEval, instanceId: instanceId, acti
1996
2296
  }) : button;
1997
2297
  }
1998
2298
 
1999
- function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, viaMenu: viaMenu, onClose: onClose}) {
2299
+ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, viaMenu: viaMenu, onClose: onClose}) {
2000
2300
  const {action: action} = actionEval, label = actionLabel(action), decls = action.params ?? [], [values, setValues] = react.useState({}), {fire: fire, pending: pending} = useFireAction({
2001
2301
  instanceId: instanceId,
2002
2302
  activity: activity,
2003
2303
  action: action.name,
2004
2304
  label: label,
2005
2305
  surface: surface,
2306
+ placement: placement,
2006
2307
  viaMenu: viaMenu
2007
2308
  }), built = buildParams(decls, values), confirm = async () => {
2008
2309
  await fire(built.params) && onClose();
@@ -2058,7 +2359,7 @@ function ParamsActionDialog({actionEval: actionEval, instanceId: instanceId, act
2058
2359
  });
2059
2360
  }
2060
2361
 
2061
- function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, chrome: chrome}) {
2362
+ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, chrome: chrome}) {
2062
2363
  const [open, setOpen] = react.useState(!1), btn = actionButtonFace({
2063
2364
  actionEval: actionEval,
2064
2365
  activityName: activity
@@ -2084,6 +2385,7 @@ function ParamsActionButton({actionEval: actionEval, instanceId: instanceId, act
2084
2385
  activity: activity,
2085
2386
  instanceId: instanceId,
2086
2387
  onClose: () => setOpen(!1),
2388
+ placement: placement,
2087
2389
  surface: surface,
2088
2390
  viaMenu: !1
2089
2391
  })
@@ -2257,7 +2559,9 @@ function FieldInput$1({kind: kind, value: value, onChange: onChange, onEnter: on
2257
2559
  });
2258
2560
 
2259
2561
  case "date":
2562
+ case "dueDate":
2260
2563
  case "datetime":
2564
+ case "dueDatetime":
2261
2565
  /* @__PURE__ */
2262
2566
  return jsxRuntime.jsx(DateFieldInput, {
2263
2567
  kind: kind,
@@ -2303,22 +2607,64 @@ function FieldInput$1({kind: kind, value: value, onChange: onChange, onEnter: on
2303
2607
  }
2304
2608
  }
2305
2609
 
2306
- function LinkChip({hint: hint, children: children, as: as = "a", ...anchorProps}) {
2610
+ function isCompact(layout) {
2611
+ return layout === "inline" || layout === "row";
2612
+ }
2613
+
2614
+ function docFaceChrome(args) {
2615
+ const {layout: layout, state: state} = args, compact = isCompact(layout);
2616
+ switch (state.kind) {
2617
+ case "linked":
2618
+ return {
2619
+ bareId: state.bareId,
2620
+ selfInset: !compact
2621
+ };
2622
+
2623
+ case "foreign":
2624
+ return {};
2625
+
2626
+ case "missing":
2627
+ return {
2628
+ tone: "caution"
2629
+ };
2630
+
2631
+ case "unopenable":
2632
+ return {};
2633
+
2634
+ case "pending":
2635
+ return compact ? {} : {
2636
+ selfInset: !0
2637
+ };
2638
+
2639
+ case "unresolvable":
2640
+ return {};
2641
+ }
2642
+ }
2643
+
2644
+ function chipPadding(fill) {
2645
+ return fill ? 3 : 2;
2646
+ }
2647
+
2648
+ function LinkChip({hint: hint, children: children, as: as = "a", fill: fill = !1, ...anchorProps}) {
2307
2649
  /* @__PURE__ */
2308
2650
  return jsxRuntime.jsx(HoverHint, {
2651
+ fill: fill,
2309
2652
  text: hint,
2310
2653
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
2311
2654
  __unstable_focusRing: !0,
2312
2655
  as: as,
2313
2656
  "data-as": "a",
2314
- padding: 2,
2657
+ padding: chipPadding(fill),
2315
2658
  radius: 3,
2316
2659
  style: {
2317
2660
  alignItems: "center",
2318
2661
  color: "inherit",
2319
2662
  display: "flex",
2320
2663
  minWidth: 0,
2321
- textDecoration: "none"
2664
+ textDecoration: "none",
2665
+ ...fill ? {
2666
+ width: "100%"
2667
+ } : {}
2322
2668
  },
2323
2669
  tone: "inherit",
2324
2670
  ...anchorProps,
@@ -2437,7 +2783,7 @@ function DocRefFace({gdr: gdr}) {
2437
2783
  });
2438
2784
  }
2439
2785
 
2440
- function ForeignRefNotice({bareId: bareId, resource: resource}) {
2786
+ function foreignNotice(bareId, resource) {
2441
2787
  const location = resource.type === "dataset" ? `another dataset (${resource.id})` : `${resource.type} (${resource.id})`;
2442
2788
  /* @__PURE__ */
2443
2789
  return jsxRuntime.jsxs(ui.Text, {
@@ -2447,44 +2793,22 @@ function ForeignRefNotice({bareId: bareId, resource: resource}) {
2447
2793
  });
2448
2794
  }
2449
2795
 
2450
- function MissingDocNotice({bareId: bareId, layout: layout}) {
2451
- return layout === "inline" ?
2452
- /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2453
- padding: 2,
2454
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2455
- align: "center",
2456
- gap: 2,
2457
- children: [
2458
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2459
- muted: !0,
2460
- size: 1,
2461
- children: /* @__PURE__ */ jsxRuntime.jsx(WarningOutline.WarningOutlineIcon, {})
2462
- }),
2463
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2464
- muted: !0,
2465
- size: 1,
2466
- children: "Document unavailable"
2467
- }) ]
2468
- })
2469
- }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
2470
- border: !0,
2471
- padding: 2,
2472
- radius: 3,
2473
- tone: "caution",
2474
- children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2475
- align: "center",
2476
- gap: 2,
2477
- children: [
2478
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2479
- size: 1,
2480
- children: /* @__PURE__ */ jsxRuntime.jsx(WarningOutline.WarningOutlineIcon, {})
2481
- }),
2482
- /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, {
2483
- muted: !0,
2484
- size: 1,
2485
- children: [ "Document unavailable — ", bareId ]
2486
- }) ]
2487
- })
2796
+ function missingNotice(bareId, layout) {
2797
+ /* @__PURE__ */
2798
+ return jsxRuntime.jsxs(ui.Flex, {
2799
+ align: "center",
2800
+ gap: 2,
2801
+ children: [
2802
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2803
+ muted: !0,
2804
+ size: 1,
2805
+ children: /* @__PURE__ */ jsxRuntime.jsx(WarningOutline.WarningOutlineIcon, {})
2806
+ }),
2807
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2808
+ muted: !0,
2809
+ size: 1,
2810
+ children: isCompact(layout) ? "Document unavailable" : `Document unavailable — ${bareId}`
2811
+ }) ]
2488
2812
  });
2489
2813
  }
2490
2814
 
@@ -2501,6 +2825,18 @@ function renderTypeIcon(icon) {
2501
2825
  return typeof icon == "function" ? react.createElement(icon) : react.isValidElement(icon) ? icon : /* @__PURE__ */ jsxRuntime.jsx(Document.DocumentIcon, {});
2502
2826
  }
2503
2827
 
2828
+ function TitleSkeleton() {
2829
+ /* @__PURE__ */
2830
+ return jsxRuntime.jsx(ui.TextSkeleton, {
2831
+ animated: !0,
2832
+ radius: 1,
2833
+ size: 1,
2834
+ style: {
2835
+ width: 96
2836
+ }
2837
+ });
2838
+ }
2839
+
2504
2840
  function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2505
2841
  const {preview: preview} = useStubValuePreview(bareId, schemaType);
2506
2842
  /* @__PURE__ */
@@ -2511,14 +2847,7 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2511
2847
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2512
2848
  size: 1,
2513
2849
  children: renderTypeIcon(schemaType.icon)
2514
- }), preview.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(ui.TextSkeleton, {
2515
- animated: !0,
2516
- radius: 1,
2517
- size: 1,
2518
- style: {
2519
- width: 96
2520
- }
2521
- }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2850
+ }), preview.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(TitleSkeleton, {}) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2522
2851
  size: 1,
2523
2852
  textOverflow: "ellipsis",
2524
2853
  weight: "medium",
@@ -2529,69 +2858,114 @@ function InlineChipFace({bareId: bareId, schemaType: schemaType}) {
2529
2858
  });
2530
2859
  }
2531
2860
 
2532
- function LinkedDocPreview({bareId: bareId, layout: layout, schemaType: schemaType}) {
2533
- const EditIntentLink = react.useMemo(() => function(linkProps) {
2534
- /* @__PURE__ */
2535
- return jsxRuntime.jsx(router.IntentLink, {
2536
- ...linkProps,
2537
- intent: "edit",
2538
- params: {
2861
+ const NOTICE_PADDING = 2;
2862
+
2863
+ function CompactDocRef({face: face, fill: fill, link: link, onClick: onClick}) {
2864
+ return link === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2865
+ padding: chipPadding(fill),
2866
+ style: fill ? {
2867
+ width: "100%"
2868
+ } : {},
2869
+ children: face.content
2870
+ }) : /* @__PURE__ */ jsxRuntime.jsx(LinkChip, {
2871
+ as: link,
2872
+ fill: fill,
2873
+ hint: "Open document",
2874
+ onClick: onClick,
2875
+ children: face.content
2876
+ });
2877
+ }
2878
+
2879
+ const PENDING_FACE_MAX_WAIT_MS = 4e3;
2880
+
2881
+ function PendingDocFace({bareId: bareId, layout: layout}) {
2882
+ const expired = useDelayedFlag(!0, PENDING_FACE_MAX_WAIT_MS), compact = isCompact(layout);
2883
+ if (expired) {
2884
+ const id = /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2885
+ muted: !0,
2886
+ size: 1,
2887
+ children: bareId
2888
+ });
2889
+ return compact ? id : /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
2890
+ padding: NOTICE_PADDING,
2891
+ children: id
2892
+ });
2893
+ }
2894
+ return compact ?
2895
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Flex, {
2896
+ align: "center",
2897
+ gap: 2,
2898
+ children: [
2899
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
2900
+ muted: !0,
2901
+ size: 1,
2902
+ children: /* @__PURE__ */ jsxRuntime.jsx(Document.DocumentIcon, {})
2903
+ }),
2904
+ /* @__PURE__ */ jsxRuntime.jsx(TitleSkeleton, {}) ]
2905
+ }) : /* @__PURE__ */ jsxRuntime.jsx(PreviewPlaceholder, {});
2906
+ }
2907
+
2908
+ function DocRefShell({face: face, layout: layout, source: source}) {
2909
+ const telemetry2 = workflowReact.useWorkflowTelemetry(), {bareId: bareId} = face, handleClick = e => {
2910
+ e.stopPropagation(), telemetry2.log(WorkflowDocumentLinkClicked, {
2911
+ source: source
2912
+ });
2913
+ }, EditIntentLink = react.useMemo(() => bareId === void 0 ? void 0 : function(linkProps) {
2914
+ /* @__PURE__ */
2915
+ return jsxRuntime.jsx(router.IntentLink, {
2916
+ ...linkProps,
2917
+ intent: "edit",
2918
+ params: {
2539
2919
  id: bareId
2540
2920
  }
2541
2921
  });
2542
2922
  }, [ bareId ]);
2543
- return layout === "inline" ? /* @__PURE__ */ jsxRuntime.jsx(LinkChip, {
2544
- as: EditIntentLink,
2545
- hint: "Open document",
2546
- onClick: e => e.stopPropagation(),
2547
- children: /* @__PURE__ */ jsxRuntime.jsx(InlineChipFace, {
2548
- bareId: bareId,
2549
- schemaType: schemaType
2550
- })
2551
- }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Card, {
2923
+ if (isCompact(layout)) /* @__PURE__ */
2924
+ return jsxRuntime.jsx(CompactDocRef, {
2925
+ face: face,
2926
+ fill: layout === "row",
2927
+ link: EditIntentLink,
2928
+ onClick: handleClick
2929
+ });
2930
+ const padding = face.selfInset === !0 ? 0 : NOTICE_PADDING;
2931
+ /* @__PURE__ */
2932
+ return jsxRuntime.jsx(ui.Card, {
2552
2933
  __unstable_focusRing: !0,
2553
- as: EditIntentLink,
2554
- border: !0,
2555
- "data-as": "a",
2556
- onClick: e => e.stopPropagation(),
2557
- padding: 1,
2934
+ border: layout === "default",
2935
+ padding: padding,
2558
2936
  radius: 3,
2559
- style: {
2560
- color: "inherit",
2561
- textDecoration: "none",
2562
- display: "block"
2937
+ tone: face.tone ?? (layout === "bare" ? "inherit" : "default"),
2938
+ ...EditIntentLink === void 0 ? {} : {
2939
+ as: EditIntentLink,
2940
+ "data-as": "a",
2941
+ onClick: handleClick,
2942
+ style: {
2943
+ color: "inherit",
2944
+ textDecoration: "none",
2945
+ display: "block"
2946
+ }
2563
2947
  },
2564
- children: /* @__PURE__ */ jsxRuntime.jsx(StubPreview, {
2565
- id: bareId,
2566
- schemaType: schemaType
2567
- })
2948
+ children: face.content
2568
2949
  });
2569
2950
  }
2570
2951
 
2571
- function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2572
- const state = useDocLink(gdr);
2952
+ function faceContent(args) {
2953
+ const {gdr: gdr, layout: layout, state: state} = args, compact = isCompact(layout);
2573
2954
  switch (state.kind) {
2574
2955
  case "linked":
2575
- /* @__PURE__ */
2576
- return jsxRuntime.jsx(LinkedDocPreview, {
2956
+ return compact ? /* @__PURE__ */ jsxRuntime.jsx(InlineChipFace, {
2577
2957
  bareId: state.bareId,
2578
- layout: layout,
2958
+ schemaType: state.schemaType
2959
+ }) : /* @__PURE__ */ jsxRuntime.jsx(StubPreview, {
2960
+ id: state.bareId,
2579
2961
  schemaType: state.schemaType
2580
2962
  });
2581
2963
 
2582
2964
  case "foreign":
2583
- /* @__PURE__ */
2584
- return jsxRuntime.jsx(ForeignRefNotice, {
2585
- bareId: state.bareId,
2586
- resource: state.resource
2587
- });
2965
+ return foreignNotice(state.bareId, state.resource);
2588
2966
 
2589
2967
  case "missing":
2590
- /* @__PURE__ */
2591
- return jsxRuntime.jsx(MissingDocNotice, {
2592
- bareId: state.bareId,
2593
- layout: layout
2594
- });
2968
+ return missingNotice(state.bareId, layout);
2595
2969
 
2596
2970
  case "unopenable":
2597
2971
  /* @__PURE__ */
@@ -2602,10 +2976,9 @@ function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2602
2976
 
2603
2977
  case "pending":
2604
2978
  /* @__PURE__ */
2605
- return jsxRuntime.jsx(ui.Text, {
2606
- muted: !0,
2607
- size: 1,
2608
- children: state.bareId
2979
+ return jsxRuntime.jsx(PendingDocFace, {
2980
+ bareId: state.bareId,
2981
+ layout: layout
2609
2982
  });
2610
2983
 
2611
2984
  case "unresolvable":
@@ -2618,6 +2991,27 @@ function DocPreviewLink({gdr: gdr, layout: layout = "default"}) {
2618
2991
  }
2619
2992
  }
2620
2993
 
2994
+ function docFace(args) {
2995
+ return {
2996
+ ...docFaceChrome(args),
2997
+ content: faceContent(args)
2998
+ };
2999
+ }
3000
+
3001
+ function DocPreviewLink({gdr: gdr, layout: layout = "default", source: source}) {
3002
+ const state = useDocLink(gdr);
3003
+ /* @__PURE__ */
3004
+ return jsxRuntime.jsx(DocRefShell, {
3005
+ face: docFace({
3006
+ gdr: gdr,
3007
+ layout: layout,
3008
+ state: state
3009
+ }),
3010
+ layout: layout,
3011
+ source: source
3012
+ });
3013
+ }
3014
+
2621
3015
  function ReleaseLink({releaseName: releaseName, label: label}) {
2622
3016
  const {basePath: basePath} = sanity.useWorkspace(), href = `${basePath === "/" ? "" : basePath}/releases/${releaseName}`;
2623
3017
  /* @__PURE__ */
@@ -2676,7 +3070,8 @@ function renderTextValue(field) {
2676
3070
 
2677
3071
  function renderSingleDocRef(field) {
2678
3072
  return field.value ? /* @__PURE__ */ jsxRuntime.jsx(DocPreviewLink, {
2679
- gdr: field.value
3073
+ gdr: field.value,
3074
+ source: "field-value"
2680
3075
  }) : /* @__PURE__ */ jsxRuntime.jsx(Empty, {});
2681
3076
  }
2682
3077
 
@@ -2698,12 +3093,15 @@ const fieldValueRenderer = {
2698
3093
  children: formatBoolean(field.value)
2699
3094
  }),
2700
3095
  date: renderTextValue,
3096
+ dueDate: renderTextValue,
2701
3097
  datetime: renderTextValue,
3098
+ dueDatetime: renderTextValue,
2702
3099
  "doc.ref": renderSingleDocRef,
2703
3100
  "doc.refs": field => field.value.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Stack, {
2704
3101
  gap: 2,
2705
3102
  children: field.value.map(ref => /* @__PURE__ */ jsxRuntime.jsx(DocPreviewLink, {
2706
- gdr: ref
3103
+ gdr: ref,
3104
+ source: "field-value"
2707
3105
  }, ref.id))
2708
3106
  }) : /* @__PURE__ */ jsxRuntime.jsx(Empty, {}),
2709
3107
  number: renderTextValue,
@@ -2925,6 +3323,27 @@ function ReadOnlyField({entry: entry}) {
2925
3323
  });
2926
3324
  }
2927
3325
 
3326
+ function useEditField(surface) {
3327
+ const {editFieldFor: editFieldFor} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry();
3328
+ return react.useCallback(async ({instanceId: instanceId, field: field, ...change}) => {
3329
+ const log = success => telemetry2.log(WorkflowFieldControlUsed, {
3330
+ instanceId: instanceId,
3331
+ surface: surface,
3332
+ fieldKind: field.type,
3333
+ success: success
3334
+ });
3335
+ try {
3336
+ await editFieldFor(instanceId, {
3337
+ ...change,
3338
+ target: workflowReact.editFieldTarget(field)
3339
+ });
3340
+ } catch (err) {
3341
+ throw log(!1), err;
3342
+ }
3343
+ log(!0);
3344
+ }, [ editFieldFor, surface, telemetry2 ]);
3345
+ }
3346
+
2928
3347
  function sameValue(a, b) {
2929
3348
  return (a ?? void 0) === (b ?? void 0);
2930
3349
  }
@@ -2961,11 +3380,12 @@ function useFieldDraft(args) {
2961
3380
  };
2962
3381
  }
2963
3382
 
2964
- function EditableFieldControl({instanceId: instanceId, field: field}) {
2965
- const {editFieldFor: editFieldFor, previewFieldFor: previewFieldFor, discardFieldPreviewFor: discardFieldPreviewFor} = useWorkflowContext(), selfActor = useSelfActor(), held = useHeldWorkflowEntry(instanceId), entry = held ? workflowEngine.resolveFieldEntry(held.instance, field) : void 0, evaluation = held?.evaluation, target = workflowReact.editFieldTarget(field), consequence = fieldConsequence(evaluation, field.name), advanceTo = advanceTargetTitle(evaluation, field.name);
3383
+ function EditableFieldControl({instanceId: instanceId, field: field, surface: surface}) {
3384
+ const {previewFieldFor: previewFieldFor, discardFieldPreviewFor: discardFieldPreviewFor} = useWorkflowContext(), editField = useEditField(surface), selfActor = useSelfActor(), held = useHeldWorkflowEntry(instanceId), entry = held ? workflowEngine.resolveFieldEntry(held.instance, field) : void 0, evaluation = held?.evaluation, target = workflowReact.editFieldTarget(field), consequence = fieldConsequence(evaluation, field.name), advanceTo = advanceTargetTitle(evaluation, field.name);
2966
3385
  /* @__PURE__ */
2967
3386
  return jsxRuntime.jsx(EditableField, {
2968
3387
  field: field,
3388
+ instanceId: instanceId,
2969
3389
  ...entry === void 0 ? {} : {
2970
3390
  entry: entry
2971
3391
  },
@@ -2978,30 +3398,33 @@ function EditableFieldControl({instanceId: instanceId, field: field}) {
2978
3398
  ...advanceTo === void 0 ? {} : {
2979
3399
  advanceTo: advanceTo
2980
3400
  },
2981
- onSave: value => editFieldFor(instanceId, {
2982
- target: target,
3401
+ onSave: value => editField({
3402
+ instanceId: instanceId,
3403
+ field: field,
2983
3404
  mode: "set",
2984
3405
  value: value
2985
- }).then(() => {}),
3406
+ }),
2986
3407
  onPreview: value => previewFieldFor(instanceId, {
2987
3408
  target: target,
2988
3409
  mode: "set",
2989
3410
  value: value
2990
3411
  }),
2991
3412
  onDiscardPreview: () => discardFieldPreviewFor(instanceId, target),
2992
- onUnset: () => editFieldFor(instanceId, {
2993
- target: target,
3413
+ onUnset: () => editField({
3414
+ instanceId: instanceId,
3415
+ field: field,
2994
3416
  mode: "unset"
2995
- }).then(() => {}),
2996
- onAppend: body => editFieldFor(instanceId, {
2997
- target: target,
3417
+ }),
3418
+ onAppend: body => editField({
3419
+ instanceId: instanceId,
3420
+ field: field,
2998
3421
  mode: "append",
2999
3422
  value: noteRow({
3000
3423
  body: body,
3001
3424
  actor: selfActor,
3002
3425
  at: /* @__PURE__ */ (new Date).toISOString()
3003
3426
  })
3004
- }).then(() => {})
3427
+ })
3005
3428
  });
3006
3429
  }
3007
3430
 
@@ -3029,28 +3452,34 @@ function EditableValueButton({children: children, hint: hint, hintDisabled: hint
3029
3452
  });
3030
3453
  }
3031
3454
 
3032
- function useSaveFailureToast() {
3033
- const toast = useClosableToast();
3034
- return err => {
3035
- console.error("[workflow-studio-plugin] field save failed:", err), toast.push({
3036
- status: "error",
3037
- title: "Saving failed",
3038
- description: describeError(err)
3039
- });
3040
- };
3455
+ function useFieldSaveReport(instanceId) {
3456
+ const report = useRejectionReport();
3457
+ return react.useMemo(() => {
3458
+ const id = instanceToastId("field-save", instanceId);
3459
+ return {
3460
+ rejected: err => report.rejected({
3461
+ err: err,
3462
+ context: "field save",
3463
+ id: id,
3464
+ refused: "This field can’t be edited right now",
3465
+ failed: "Failed to save the field"
3466
+ }),
3467
+ dismiss: () => report.dismiss(id)
3468
+ };
3469
+ }, [ report, instanceId ]);
3041
3470
  }
3042
3471
 
3043
- function useSaveField(args = {}) {
3044
- const reportFailure = useSaveFailureToast(), [saving, setSaving] = react.useState(!1);
3472
+ function useSaveField(args) {
3473
+ const report = useFieldSaveReport(args.instanceId), [saving, setSaving] = react.useState(!1);
3045
3474
  return {
3046
3475
  saving: saving,
3047
3476
  save: async commit => {
3048
3477
  if (!saving) {
3049
3478
  setSaving(!0);
3050
3479
  try {
3051
- await commit(), args.onSaved?.();
3480
+ await commit(), report.dismiss(), args.onSaved?.();
3052
3481
  } catch (err) {
3053
- reportFailure(err);
3482
+ report.rejected(err);
3054
3483
  } finally {
3055
3484
  setSaving(!1);
3056
3485
  }
@@ -3060,7 +3489,9 @@ function useSaveField(args = {}) {
3060
3489
  }
3061
3490
 
3062
3491
  function useActorPick(args) {
3063
- const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField();
3492
+ const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3493
+ instanceId: args.instanceId
3494
+ });
3064
3495
  return {
3065
3496
  saving: saving,
3066
3497
  pick: member => {
@@ -3069,9 +3500,10 @@ function useActorPick(args) {
3069
3500
  };
3070
3501
  }
3071
3502
 
3072
- function ActorField({field: field, onSave: onSave, onUnset: onUnset}) {
3503
+ function ActorField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3073
3504
  const [open, setOpen] = react.useState(!1), cur = isActorShape(field.value) ? field.value : null, {pick: pick} = useActorPick({
3074
3505
  current: cur,
3506
+ instanceId: instanceId,
3075
3507
  onSave: onSave,
3076
3508
  onUnset: onUnset
3077
3509
  }), pickAndClose = member => {
@@ -3106,7 +3538,9 @@ function ActorField({field: field, onSave: onSave, onUnset: onUnset}) {
3106
3538
  }
3107
3539
 
3108
3540
  function useAssigneePick(args) {
3109
- const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField();
3541
+ const {current: current, onSave: onSave, onUnset: onUnset} = args, {saving: saving, save: save} = useSaveField({
3542
+ instanceId: args.instanceId
3543
+ });
3110
3544
  return {
3111
3545
  saving: saving,
3112
3546
  pick: assignee => {
@@ -3121,9 +3555,10 @@ function useAssigneePick(args) {
3121
3555
  };
3122
3556
  }
3123
3557
 
3124
- function AssigneeField({field: field, onSave: onSave, onUnset: onUnset}) {
3558
+ function AssigneeField({field: field, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3125
3559
  const [open, setOpen] = react.useState(!1), cur = isAssigneeShape(field.value) ? field.value : null, {pick: pick} = useAssigneePick({
3126
3560
  current: cur,
3561
+ instanceId: instanceId,
3127
3562
  onSave: onSave,
3128
3563
  onUnset: onUnset
3129
3564
  }), pickAndClose = assignee => {
@@ -3163,8 +3598,10 @@ function arrayRowsOf(value) {
3163
3598
  return Array.isArray(value) ? value.filter(row => typeof row == "object" && row !== null) : [];
3164
3599
  }
3165
3600
 
3166
- function NotesField({field: field, onAppend: onAppend}) {
3167
- const [body, setBody] = react.useState(""), {saving: saving, save: save} = useSaveField(), rows = arrayRowsOf(field.value), trimmed = body.trim(), add = () => {
3601
+ function NotesField({field: field, instanceId: instanceId, onAppend: onAppend}) {
3602
+ const [body, setBody] = react.useState(""), {saving: saving, save: save} = useSaveField({
3603
+ instanceId: instanceId
3604
+ }), rows = arrayRowsOf(field.value), trimmed = body.trim(), add = () => {
3168
3605
  saving || trimmed === "" || (setBody(""), save(async () => {
3169
3606
  try {
3170
3607
  await onAppend(trimmed);
@@ -3205,11 +3642,13 @@ function NotesField({field: field, onAppend: onAppend}) {
3205
3642
  });
3206
3643
  }
3207
3644
 
3208
- function DateField({field: field, onSave: onSave, onUnset: onUnset}) {
3209
- const {save: save} = useSaveField();
3645
+ function DateField({field: field, kind: kind, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3646
+ const {save: save} = useSaveField({
3647
+ instanceId: instanceId
3648
+ });
3210
3649
  /* @__PURE__ */
3211
3650
  return jsxRuntime.jsx(DateFieldInput, {
3212
- kind: field.type === "datetime" ? "datetime" : "date",
3651
+ kind: kind,
3213
3652
  onChange: next => {
3214
3653
  save(() => next === null && onUnset !== void 0 ? onUnset() : onSave(next));
3215
3654
  },
@@ -3217,8 +3656,10 @@ function DateField({field: field, onSave: onSave, onUnset: onUnset}) {
3217
3656
  });
3218
3657
  }
3219
3658
 
3220
- function DocRefField({field: field, entry: entry, onSave: onSave, onUnset: onUnset}) {
3221
- const {save: save} = useSaveField(), value = workflowEngine.isGdr(field.value) ? field.value : null, types2 = entry !== void 0 && workflowEngine.isSingleDocRefEntry(entry) ? entry.types : void 0;
3659
+ function DocRefField({field: field, entry: entry, instanceId: instanceId, onSave: onSave, onUnset: onUnset}) {
3660
+ const {save: save} = useSaveField({
3661
+ instanceId: instanceId
3662
+ }), value = workflowEngine.isGdr(field.value) ? field.value : null, types2 = entry !== void 0 && workflowEngine.isSingleDocRefEntry(entry) ? entry.types : void 0;
3222
3663
  return field.editable ? /* @__PURE__ */ jsxRuntime.jsx(DocPicker, {
3223
3664
  onChange: ref => {
3224
3665
  save(() => ref === null && onUnset !== void 0 ? onUnset() : onSave(ref));
@@ -3233,8 +3674,10 @@ function DocRefField({field: field, entry: entry, onSave: onSave, onUnset: onUns
3233
3674
  });
3234
3675
  }
3235
3676
 
3236
- function BooleanSwitchField({field: field, onSave: onSave}) {
3237
- const {save: save} = useSaveField(), committed = field.value === !0;
3677
+ function BooleanSwitchField({field: field, instanceId: instanceId, onSave: onSave}) {
3678
+ const {save: save} = useSaveField({
3679
+ instanceId: instanceId
3680
+ }), committed = field.value === !0;
3238
3681
  /* @__PURE__ */
3239
3682
  return jsxRuntime.jsx(ui.Flex, {
3240
3683
  align: "center",
@@ -3247,8 +3690,10 @@ function BooleanSwitchField({field: field, onSave: onSave}) {
3247
3690
  });
3248
3691
  }
3249
3692
 
3250
- function ChoiceField({field: field, options: options, onSave: onSave, onUnset: onUnset}) {
3251
- const {save: save} = useSaveField();
3693
+ function ChoiceField({field: field, instanceId: instanceId, options: options, onSave: onSave, onUnset: onUnset}) {
3694
+ const {save: save} = useSaveField({
3695
+ instanceId: instanceId
3696
+ });
3252
3697
  /* @__PURE__ */
3253
3698
  return jsxRuntime.jsx(ChoiceSelect, {
3254
3699
  onChange: value => {
@@ -3322,8 +3767,8 @@ function FieldEditRow({field: field, onSave: onSave, onCancel: onCancel, saving:
3322
3767
  });
3323
3768
  }
3324
3769
 
3325
- function GenericField({field: field, onSave: onSave, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3326
- const reportFailure = useSaveFailureToast(), canSave = value => scalarValidationIssue({
3770
+ function GenericField({field: field, onSave: onSave, instanceId: instanceId, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3771
+ const report = useFieldSaveReport(instanceId), canSave = value => scalarValidationIssue({
3327
3772
  kind: field.type,
3328
3773
  label: field.title ?? field.name,
3329
3774
  validation: field.validation,
@@ -3334,7 +3779,7 @@ function GenericField({field: field, onSave: onSave, onPreview: onPreview, onDis
3334
3779
  onPreview: onPreview,
3335
3780
  onDiscardPreview: onDiscardPreview,
3336
3781
  canSave: canSave,
3337
- onSaveFailure: reportFailure
3782
+ onSaveFailure: report.rejected
3338
3783
  }), validationIssue = scalarValidationIssue({
3339
3784
  kind: field.type,
3340
3785
  label: field.title ?? field.name,
@@ -3380,9 +3825,10 @@ function AdvanceHint({stageTitle: stageTitle2}) {
3380
3825
  });
3381
3826
  }
3382
3827
 
3383
- function arrayArm({field: field, entry: entry, onAppend: onAppend}) {
3828
+ function arrayArm({field: field, entry: entry, instanceId: instanceId, onAppend: onAppend}) {
3384
3829
  return field.editable && onAppend !== void 0 && entry !== void 0 && workflowEngine.isNotesEntry(entry) ? /* @__PURE__ */ jsxRuntime.jsx(NotesField, {
3385
3830
  field: field,
3831
+ instanceId: instanceId,
3386
3832
  onAppend: onAppend
3387
3833
  }) : /* @__PURE__ */ jsxRuntime.jsx(ArrayValueRows, {
3388
3834
  rows: arrayRowsOf(field.value)
@@ -3390,7 +3836,7 @@ function arrayArm({field: field, entry: entry, onAppend: onAppend}) {
3390
3836
  }
3391
3837
 
3392
3838
  function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview}) {
3393
- const {field: field} = arm;
3839
+ const {field: field, instanceId: instanceId} = arm;
3394
3840
  return field.type === "actor" ? /* @__PURE__ */ jsxRuntime.jsx(ActorField, {
3395
3841
  ...arm,
3396
3842
  onUnset: onUnset
@@ -3403,11 +3849,13 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
3403
3849
  }) : field.type === "array" ? arrayArm({
3404
3850
  field: field,
3405
3851
  entry: entry,
3852
+ instanceId: instanceId,
3406
3853
  onAppend: onAppend
3407
3854
  }) : field.type === "boolean" && field.editable ? /* @__PURE__ */ jsxRuntime.jsx(BooleanSwitchField, {
3408
3855
  ...arm
3409
- }) : (field.type === "date" || field.type === "datetime") && field.editable ? /* @__PURE__ */ jsxRuntime.jsx(DateField, {
3856
+ }) : isDateFieldKind(field.type) && field.editable ? /* @__PURE__ */ jsxRuntime.jsx(DateField, {
3410
3857
  ...arm,
3858
+ kind: field.type,
3411
3859
  onUnset: onUnset
3412
3860
  }) : workflowEngine.isSingleDocRefKind(field.type) ? /* @__PURE__ */ jsxRuntime.jsx(DocRefField, {
3413
3861
  ...arm,
@@ -3420,9 +3868,10 @@ function fieldArm({arm: arm, entry: entry, onUnset: onUnset, onAppend: onAppend,
3420
3868
  });
3421
3869
  }
3422
3870
 
3423
- function EditableField({field: field, entry: entry, description: description, onSave: onSave, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview, consequence: consequence, advanceTo: advanceTo}) {
3871
+ function EditableField({field: field, entry: entry, description: description, instanceId: instanceId, onSave: onSave, onUnset: onUnset, onAppend: onAppend, onPreview: onPreview, onDiscardPreview: onDiscardPreview, consequence: consequence, advanceTo: advanceTo}) {
3424
3872
  const label = field.title ?? field.name, arm = {
3425
3873
  field: field,
3874
+ instanceId: instanceId,
3426
3875
  onSave: onSave
3427
3876
  }, content2 = entry?.options === void 0 ? fieldArm({
3428
3877
  arm: arm,
@@ -3454,11 +3903,15 @@ function EditableField({field: field, entry: entry, description: description, on
3454
3903
  });
3455
3904
  }
3456
3905
 
3457
- function MetaRow({label: label, children: children}) {
3906
+ function MetaRow({children: children, fillHeight: fillHeight, label: label}) {
3458
3907
  /* @__PURE__ */
3459
3908
  return jsxRuntime.jsxs(ui.Flex, {
3460
- align: "flex-start",
3461
3909
  gap: 4,
3910
+ ...fillHeight ? {
3911
+ flex: 1
3912
+ } : {
3913
+ align: "flex-start"
3914
+ },
3462
3915
  children: [
3463
3916
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
3464
3917
  style: {
@@ -3531,27 +3984,6 @@ function ActivityStatusIcon({status: status}) {
3531
3984
  });
3532
3985
  }
3533
3986
 
3534
- function LoadingRow({label: label, padding: padding}) {
3535
- /* @__PURE__ */
3536
- return jsxRuntime.jsxs(ui.Flex, {
3537
- align: "center",
3538
- gap: 2,
3539
- ...padding === void 0 ? {} : {
3540
- padding: padding
3541
- },
3542
- children: [
3543
- /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
3544
- muted: !0,
3545
- size: 1
3546
- }),
3547
- /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
3548
- muted: !0,
3549
- size: 1,
3550
- children: label
3551
- }) ]
3552
- });
3553
- }
3554
-
3555
3987
  function useSpaceToken(index) {
3556
3988
  const value = ui.useTheme_v2().space[index];
3557
3989
  if (value === void 0) throw new Error(`theme is missing space token ${index}`);
@@ -3598,6 +4030,46 @@ function useBadgeCapTrim() {
3598
4030
  };
3599
4031
  }
3600
4032
 
4033
+ function SpinnerSlot({busy: busy}) {
4034
+ const size = useTextIconSize(), trim = useCapTrimFor(size);
4035
+ /* @__PURE__ */
4036
+ return jsxRuntime.jsx(ui.Flex, {
4037
+ align: "center",
4038
+ flex: "none",
4039
+ justify: "center",
4040
+ style: {
4041
+ height: size,
4042
+ marginBottom: trim,
4043
+ marginTop: trim,
4044
+ width: size
4045
+ },
4046
+ children: busy ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
4047
+ muted: !0,
4048
+ size: 1
4049
+ }) : null
4050
+ });
4051
+ }
4052
+
4053
+ function LoadingRow({label: label, padding: padding}) {
4054
+ /* @__PURE__ */
4055
+ return jsxRuntime.jsxs(ui.Flex, {
4056
+ align: "center",
4057
+ gap: 2,
4058
+ ...padding === void 0 ? {} : {
4059
+ padding: padding
4060
+ },
4061
+ children: [
4062
+ /* @__PURE__ */ jsxRuntime.jsx(SpinnerSlot, {
4063
+ busy: !0
4064
+ }),
4065
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
4066
+ muted: !0,
4067
+ size: 1,
4068
+ children: label
4069
+ }) ]
4070
+ });
4071
+ }
4072
+
3601
4073
  function AvatarDisplay({avatar: avatar, "aria-label": ariaLabel}) {
3602
4074
  const trim = useAvatarCapTrim();
3603
4075
  /* @__PURE__ */
@@ -3678,8 +4150,8 @@ function UserAvatarGroup({ids: ids, hint: hint}) {
3678
4150
  });
3679
4151
  }
3680
4152
 
3681
- function AssignActivityControl({instanceId: instanceId, state: state, assigneeIds: assigneeIds = []}) {
3682
- const {editFieldFor: editFieldFor} = useWorkflowContext(), toast = useClosableToast(), [open, setOpen] = react.useState(!1), [busy, setBusy] = react.useState(!1);
4153
+ function AssignActivityControl({instanceId: instanceId, state: state, surface: surface, assigneeIds: assigneeIds = []}) {
4154
+ const editField = useEditField(surface), report = useRejectionReport(), [open, setOpen] = react.useState(!1), [busy, setBusy] = react.useState(!1);
3683
4155
  if (state.kind === "none") return assigneeIds.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(AvatarDisplay, {
3684
4156
  "aria-label": "Assignees",
3685
4157
  avatar: /* @__PURE__ */ jsxRuntime.jsx(UserAvatarGroup, {
@@ -3700,23 +4172,27 @@ function AssignActivityControl({instanceId: instanceId, state: state, assigneeId
3700
4172
  if (!(busy || field === void 0)) {
3701
4173
  setBusy(!0);
3702
4174
  try {
3703
- selectedIds.has(memberId) ? await editFieldFor(instanceId, {
3704
- target: workflowReact.editFieldTarget(field),
4175
+ selectedIds.has(memberId) ? await editField({
4176
+ instanceId: instanceId,
4177
+ field: field,
3705
4178
  mode: "set",
3706
4179
  value: members.filter(m => !(m.type === "user" && m.id === memberId))
3707
- }) : await editFieldFor(instanceId, {
3708
- target: workflowReact.editFieldTarget(field),
4180
+ }) : await editField({
4181
+ instanceId: instanceId,
4182
+ field: field,
3709
4183
  mode: "append",
3710
4184
  value: {
3711
4185
  type: "user",
3712
4186
  id: memberId
3713
4187
  }
3714
- });
4188
+ }), report.dismiss(instanceToastId("assign", instanceId));
3715
4189
  } catch (err) {
3716
- toast.push({
3717
- status: "error",
3718
- title: "Assigning failed",
3719
- description: describeError(err)
4190
+ report.rejected({
4191
+ err: err,
4192
+ context: "assignment",
4193
+ id: instanceToastId("assign", instanceId),
4194
+ refused: "Assignees can’t be changed right now",
4195
+ failed: "Failed to update assignees"
3720
4196
  });
3721
4197
  } finally {
3722
4198
  setBusy(!1);
@@ -3783,6 +4259,11 @@ function appendTargetFor(targets, filter) {
3783
4259
  if (filter?.kind !== "mine") return filter?.kind === "activity" ? targets.find(t => t.activity === filter.activity) : filter?.kind === "field" ? targets.find(t => t.scope === filter.scope && t.field === filter.field && (filter.scope !== "activity" || t.activity === filter.activity)) : targets[0];
3784
4260
  }
3785
4261
 
4262
+ function canRemoveRow(args) {
4263
+ const {editTarget: editTarget, appendTarget: appendTarget} = args;
4264
+ return editTarget === void 0 || appendTarget === void 0 ? !1 : editTarget.scope === appendTarget.scope && editTarget.field === appendTarget.field && editTarget.activity === appendTarget.activity;
4265
+ }
4266
+
3786
4267
  function looksLikeDefinition(value) {
3787
4268
  return typeof value == "object" && value !== null && Array.isArray(value.stages);
3788
4269
  }
@@ -4024,6 +4505,21 @@ function AddItemControl({onAdd: onAdd, button: button, busy: busy}) {
4024
4505
  });
4025
4506
  }
4026
4507
 
4508
+ function RemoveItemButton({onClick: onClick}) {
4509
+ /* @__PURE__ */
4510
+ return jsxRuntime.jsx(HoverHint, {
4511
+ text: "Remove item",
4512
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
4513
+ "aria-label": "Remove item",
4514
+ fontSize: 1,
4515
+ icon: Close.CloseIcon,
4516
+ mode: "bleed",
4517
+ onClick: onClick,
4518
+ padding: 2
4519
+ })
4520
+ });
4521
+ }
4522
+
4027
4523
  function BreadcrumbTail({segments: segments, style: style}) {
4028
4524
  /* @__PURE__ */
4029
4525
  return jsxRuntime.jsx(ui.Text, {
@@ -4090,7 +4586,7 @@ function WorkRow({lead: lead, children: children, end: end, onOpen: onOpen}) {
4090
4586
  });
4091
4587
  }
4092
4588
 
4093
- function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch}) {
4589
+ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle, onPatch: onPatch, onRemove: onRemove}) {
4094
4590
  const {canClick: canClick, hint: hint} = rowInteractivity(row), editable = row.editTarget !== void 0, checkbox = /* @__PURE__ */ jsxRuntime.jsx(TodoCheckbox, {
4095
4591
  canClick: canClick,
4096
4592
  checked: isTodoDone(row.item),
@@ -4109,6 +4605,8 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
4109
4605
  editable: editable,
4110
4606
  item: row.item,
4111
4607
  onPatch: onPatch
4608
+ }), onRemove === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(RemoveItemButton, {
4609
+ onClick: onRemove
4112
4610
  }) ]
4113
4611
  }),
4114
4612
  lead:
@@ -4135,37 +4633,41 @@ function TodoItemRowView({breadcrumb: breadcrumb, row: row, onToggle: onToggle,
4135
4633
  }
4136
4634
 
4137
4635
  function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, surface: surface}) {
4138
- const {instance: instance} = entry, definition = useDefinition(entry), {editFieldFor: editFieldFor, fireActionFor: fireActionFor} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), toast = useClosableToast(), [busy, setBusy] = react.useState(!1), derivation = deriveTodoItems(entry), rows = derivation.rows.filter(row => rowMatches(row, filter)), appendTarget = appendTargetFor(derivation.appendTargets, filter), run = async op => {
4636
+ const {instance: instance} = entry, definition = useDefinition(entry), {editFieldFor: editFieldFor, fireActionFor: fireActionFor} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), report = useRejectionReport(), [busy, setBusy] = react.useState(!1), derivation = deriveTodoItems(entry), rows = derivation.rows.filter(row => rowMatches(row, filter)), appendTarget = appendTargetFor(derivation.appendTargets, filter), run = async op => {
4139
4637
  if (busy) return !1;
4140
4638
  setBusy(!0);
4141
4639
  try {
4142
- return await op(), !0;
4640
+ return await op(), report.dismiss(instanceToastId("todo-write", instance._id)),
4641
+ !0;
4143
4642
  } catch (err) {
4144
- return toast.push({
4145
- status: "error",
4146
- title: "Updating to-dos failed",
4147
- description: describeError(err)
4643
+ return report.rejected({
4644
+ err: err,
4645
+ context: "to-do write",
4646
+ id: instanceToastId("todo-write", instance._id),
4647
+ refused: "To-dos can’t be changed right now",
4648
+ failed: "Failed to update to-dos"
4148
4649
  }), !1;
4149
4650
  } finally {
4150
4651
  setBusy(!1);
4151
4652
  }
4152
- }, patchItem = (row, patch) => {
4153
- if (!row.editTarget) return Promise.resolve();
4154
- const target = row.editTarget, next = row.items.map(it => it._key === row.item._key ? {
4155
- ...it,
4156
- ...patch
4157
- } : it);
4653
+ }, writeItems = (row, nextItems) => {
4654
+ if (!row.editTarget) return Promise.resolve(!1);
4655
+ const target = row.editTarget;
4158
4656
  return run(() => editFieldFor(instance._id, {
4159
4657
  target: target,
4160
4658
  mode: "set",
4161
- value: next
4659
+ value: nextItems()
4162
4660
  }));
4163
- }, toggleRow = async row => {
4661
+ }, patchItem = (row, patch) => writeItems(row, () => todoItemsPatched({
4662
+ items: row.items,
4663
+ key: row.item._key,
4664
+ patch: patch
4665
+ })), toggleRow = async row => {
4164
4666
  if (busy) return;
4165
4667
  if (row.editTarget) {
4166
4668
  const done = !isTodoDone(row.item), success = await patchItem(row, {
4167
4669
  status: toggledTodoStatus(row.item)
4168
- }) === !0;
4670
+ });
4169
4671
  telemetry2.log(WorkflowTodoToggled, {
4170
4672
  instanceId: instance._id,
4171
4673
  surface: surface,
@@ -4189,14 +4691,44 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, su
4189
4691
  success: success
4190
4692
  });
4191
4693
  }
4192
- }, addItem = label => appendTarget ? run(() => editFieldFor(instance._id, {
4193
- target: appendTarget,
4194
- mode: "append",
4195
- value: {
4196
- label: label,
4197
- status: "open"
4198
- }
4199
- })) : Promise.resolve(!1);
4694
+ }, addItem = async label => {
4695
+ if (busy || !appendTarget) return !1;
4696
+ const success = await run(() => editFieldFor(instance._id, {
4697
+ target: appendTarget,
4698
+ mode: "append",
4699
+ value: {
4700
+ label: label,
4701
+ status: "open"
4702
+ }
4703
+ }));
4704
+ return telemetry2.log(WorkflowTodoEdited, {
4705
+ instanceId: instance._id,
4706
+ surface: surface,
4707
+ kind: "add",
4708
+ success: success
4709
+ }), success;
4710
+ }, editRow = async (row, patch) => {
4711
+ if (busy || row.editTarget === void 0) return;
4712
+ const success = await patchItem(row, patch);
4713
+ telemetry2.log(WorkflowTodoEdited, {
4714
+ instanceId: instance._id,
4715
+ surface: surface,
4716
+ kind: todoEditKind(patch),
4717
+ success: success
4718
+ });
4719
+ }, removeRow = async row => {
4720
+ if (busy || row.editTarget === void 0) return;
4721
+ const success = await writeItems(row, () => todoItemsWithout({
4722
+ items: row.items,
4723
+ key: row.item._key
4724
+ }));
4725
+ telemetry2.log(WorkflowTodoEdited, {
4726
+ instanceId: instance._id,
4727
+ surface: surface,
4728
+ kind: "remove",
4729
+ success: success
4730
+ });
4731
+ };
4200
4732
  if (rows.length === 0 && !appendTarget) return null;
4201
4733
  const rowKey = row => `${row.scope}:${row.activityName ?? ""}:${row.fieldName}:${row.item._key}`, rowBreadcrumb = row => {
4202
4734
  if (breadcrumb === void 0 || row.activityName === void 0) return breadcrumb;
@@ -4216,8 +4748,16 @@ function TodoItemsList({breadcrumb: breadcrumb, entry: entry, filter: filter, su
4216
4748
  children: rows.map(row => /* @__PURE__ */ jsxRuntime.jsx(TodoItemRowView, {
4217
4749
  breadcrumb: rowBreadcrumb(row),
4218
4750
  onPatch: patch => {
4219
- patchItem(row, patch);
4751
+ editRow(row, patch);
4220
4752
  },
4753
+ ...canRemoveRow({
4754
+ editTarget: row.editTarget,
4755
+ appendTarget: appendTarget
4756
+ }) ? {
4757
+ onRemove: () => {
4758
+ removeRow(row);
4759
+ }
4760
+ } : {},
4221
4761
  onToggle: () => {
4222
4762
  toggleRow(row);
4223
4763
  },
@@ -4305,7 +4845,7 @@ const ACTIONS_MENU_TRIGGER = {
4305
4845
  padding: 2
4306
4846
  };
4307
4847
 
4308
- function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, onCollectParams: onCollectParams}) {
4848
+ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activity: activity, surface: surface, placement: placement, onCollectParams: onCollectParams}) {
4309
4849
  const rowKind = actionRowKind(actionEval), label = actionTriggerLabel(actionEval), {tone: tone, icon: icon} = actionButtonFace({
4310
4850
  actionEval: actionEval,
4311
4851
  activityName: activity
@@ -4317,6 +4857,7 @@ function ActionMenuItem({actionEval: actionEval, instanceId: instanceId, activit
4317
4857
  action: actionEval.action.name,
4318
4858
  label: label,
4319
4859
  surface: surface,
4860
+ placement: placement,
4320
4861
  viaMenu: !0
4321
4862
  });
4322
4863
  return rowKind.kind === "disabled" ? /* @__PURE__ */ jsxRuntime.jsx(ui.MenuItem, {
@@ -4353,7 +4894,7 @@ function rowShield(menuButton) {
4353
4894
  });
4354
4895
  }
4355
4896
 
4356
- function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, surface: surface, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4897
+ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity: activity, label: label, surface: surface, placement: placement, mode: mode = "ghost", inButtonCard: inButtonCard = !1}) {
4357
4898
  const [paramsAction, setParamsAction] = react.useState(void 0), clusterPending = useActionClusterPending(), menuId = react.useId(), menuButton = /* @__PURE__ */ jsxRuntime.jsx(ui.MenuButton, {
4358
4899
  button: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
4359
4900
  ...ACTIONS_MENU_TRIGGER,
@@ -4373,6 +4914,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4373
4914
  activity: activity,
4374
4915
  instanceId: instanceId,
4375
4916
  onCollectParams: () => setParamsAction(a),
4917
+ placement: placement,
4376
4918
  surface: surface
4377
4919
  }, a.action.name))
4378
4920
  }),
@@ -4390,6 +4932,7 @@ function ActionsMenuButton({actions: actions, instanceId: instanceId, activity:
4390
4932
  activity: activity,
4391
4933
  instanceId: instanceId,
4392
4934
  onClose: () => setParamsAction(void 0),
4935
+ placement: placement,
4393
4936
  surface: surface,
4394
4937
  viaMenu: !0
4395
4938
  })
@@ -4408,7 +4951,8 @@ function TerminalFooter({actions: actions, activity: activity, instanceId: insta
4408
4951
  actionEval: only,
4409
4952
  activity: activity,
4410
4953
  instanceId: instanceId,
4411
- surface: "terminal-footer"
4954
+ placement: "terminal-footer",
4955
+ surface: "activity-dialog"
4412
4956
  })
4413
4957
  }) : /* @__PURE__ */ jsxRuntime.jsx(FittedActions, {
4414
4958
  actions: actions,
@@ -4463,14 +5007,16 @@ function FittedActions({actions: actions, activity: activity, instanceId: instan
4463
5007
  actionEval: a,
4464
5008
  activity: activity,
4465
5009
  instanceId: instanceId,
4466
- surface: "terminal-footer"
5010
+ placement: "terminal-footer",
5011
+ surface: "activity-dialog"
4467
5012
  }, a.action.name)), overflow.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(ActionsMenuButton, {
4468
5013
  actions: overflow,
4469
5014
  activity: activity,
4470
5015
  instanceId: instanceId,
4471
5016
  label: MORE_ACTIONS_LABEL,
4472
5017
  mode: "default",
4473
- surface: "terminal-footer"
5018
+ placement: "terminal-footer",
5019
+ surface: "activity-dialog"
4474
5020
  }) : null ]
4475
5021
  }) ]
4476
5022
  });
@@ -4605,7 +5151,8 @@ function MetaBlock({detail: detail, definition: definition, document: document,
4605
5151
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
4606
5152
  children: /* @__PURE__ */ jsxRuntime.jsx(DocPreviewLink, {
4607
5153
  gdr: document,
4608
- layout: "inline"
5154
+ layout: "inline",
5155
+ source: "dialog-subject"
4609
5156
  })
4610
5157
  })
4611
5158
  }) : null,
@@ -4629,7 +5176,8 @@ function TopActions({detail: detail, definition: definition, instanceId: instanc
4629
5176
  actionEval: a,
4630
5177
  activity: activityName,
4631
5178
  instanceId: instanceId,
4632
- surface: "dialog-strip"
5179
+ placement: "dialog-strip",
5180
+ surface: "activity-dialog"
4633
5181
  }, a.action.name)), p.manualTarget ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
4634
5182
  as: "a",
4635
5183
  fontSize: 1,
@@ -4687,7 +5235,8 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
4687
5235
  state: {
4688
5236
  kind: "editable",
4689
5237
  field: assignField
4690
- }
5238
+ },
5239
+ surface: "activity-dialog"
4691
5240
  }) : null ]
4692
5241
  }) ]
4693
5242
  });
@@ -4695,7 +5244,8 @@ function FieldRow({field: field, detail: detail, entry: entry, activityName: act
4695
5244
  const editable = detail.editableByName.get(field.name);
4696
5245
  return editable ? /* @__PURE__ */ jsxRuntime.jsx(EditableFieldControl, {
4697
5246
  field: editable,
4698
- instanceId: instanceId
5247
+ instanceId: instanceId,
5248
+ surface: "activity-dialog"
4699
5249
  }) : /* @__PURE__ */ jsxRuntime.jsxs(ui.Stack, {
4700
5250
  gap: 2,
4701
5251
  children: [ head,
@@ -4788,26 +5338,6 @@ function CountedLabel({count: count, label: label}) {
4788
5338
  });
4789
5339
  }
4790
5340
 
4791
- function SpinnerSlot({busy: busy}) {
4792
- const size = useTextIconSize(), trim = useCapTrimFor(size);
4793
- /* @__PURE__ */
4794
- return jsxRuntime.jsx(ui.Flex, {
4795
- align: "center",
4796
- flex: "none",
4797
- justify: "center",
4798
- style: {
4799
- height: size,
4800
- marginBottom: trim,
4801
- marginTop: trim,
4802
- width: size
4803
- },
4804
- children: busy ? /* @__PURE__ */ jsxRuntime.jsx(ui.Spinner, {
4805
- muted: !0,
4806
- size: 1
4807
- }) : null
4808
- });
4809
- }
4810
-
4811
5341
  function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4812
5342
  /* @__PURE__ */
4813
5343
  return jsxRuntime.jsxs(ui.Flex, {
@@ -4823,14 +5353,100 @@ function GroupHeading({busy: busy, count: count, title: title, end: end}) {
4823
5353
  weight: "semibold",
4824
5354
  children: title
4825
5355
  })
4826
- }), busy === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(SpinnerSlot, {
4827
- busy: busy
4828
- }), end === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
4829
- children: [
4830
- /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
4831
- flex: 1
4832
- }), end ]
4833
- }) ]
5356
+ }), busy === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(SpinnerSlot, {
5357
+ busy: busy
5358
+ }), end === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, {
5359
+ children: [
5360
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
5361
+ flex: 1
5362
+ }), end ]
5363
+ }) ]
5364
+ });
5365
+ }
5366
+
5367
+ function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dueDates: dueDates}) {
5368
+ const editField = useEditField(surface), {save: save} = useSaveField({
5369
+ instanceId: instanceId
5370
+ }), [open, setOpen] = react.useState(!1), raw = dateControlValue({
5371
+ state: state,
5372
+ dueDates: dueDates
5373
+ }), display = raw === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
5374
+ muted: !0,
5375
+ size: 1,
5376
+ children: formatDate(raw)
5377
+ });
5378
+ if (state.kind === "none") return display;
5379
+ if (state.kind === "closed") return display === null ? null : /* @__PURE__ */ jsxRuntime.jsx(HoverHint, {
5380
+ text: state.reason,
5381
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
5382
+ "aria-label": "Date",
5383
+ as: "span",
5384
+ fontSize: 1,
5385
+ mode: "bleed",
5386
+ onClick: stopRowClick,
5387
+ onMouseDown: stopRowMouseDown,
5388
+ padding: 2,
5389
+ children: display
5390
+ })
5391
+ });
5392
+ const kind = dateControlKind(state), commit = (mode, value) => {
5393
+ state.kind === "editable" && save(() => editField({
5394
+ instanceId: instanceId,
5395
+ field: state.field,
5396
+ ...mode === "set" ? {
5397
+ mode: mode,
5398
+ value: value
5399
+ } : {
5400
+ mode: mode
5401
+ }
5402
+ }));
5403
+ }, handleClick = e => {
5404
+ e.stopPropagation(), setOpen(v => !v);
5405
+ };
5406
+ /* @__PURE__ */
5407
+ return jsxRuntime.jsx(DismissablePopover, {
5408
+ content: /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
5409
+ onClick: stopRowClick,
5410
+ children: state.kind === "loading" || kind === void 0 ? /* @__PURE__ */ jsxRuntime.jsx(LoadingRow, {
5411
+ label: "Loading…",
5412
+ padding: 3
5413
+ }) : /* @__PURE__ */ jsxRuntime.jsx(workflowComponents.ClearableDatePicker, {
5414
+ clearLabel: "Clear date",
5415
+ onClear: () => {
5416
+ setOpen(!1), commit("unset");
5417
+ },
5418
+ onPick: next => {
5419
+ hasTimeOfDay(kind) || setOpen(!1), commit("set", serializeDateFieldValue(next, kind));
5420
+ },
5421
+ selectTime: hasTimeOfDay(kind),
5422
+ value: parseDateFieldValue(state.field.value, kind)
5423
+ })
5424
+ }),
5425
+ onDismiss: () => setOpen(!1),
5426
+ open: open,
5427
+ children: display ? /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
5428
+ "aria-label": "Change date",
5429
+ as: "span",
5430
+ fontSize: 1,
5431
+ mode: "bleed",
5432
+ onClick: handleClick,
5433
+ onMouseDown: stopRowMouseDown,
5434
+ padding: 2,
5435
+ children: display
5436
+ }) : /* @__PURE__ */ jsxRuntime.jsx(HoverHint, {
5437
+ disabled: open,
5438
+ text: "Set date",
5439
+ children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
5440
+ "aria-label": "Set date",
5441
+ as: "span",
5442
+ fontSize: 1,
5443
+ icon: Calendar.CalendarIcon,
5444
+ mode: "bleed",
5445
+ onClick: handleClick,
5446
+ onMouseDown: stopRowMouseDown,
5447
+ padding: 2
5448
+ })
5449
+ })
4834
5450
  });
4835
5451
  }
4836
5452
 
@@ -4864,7 +5480,7 @@ function ActivityRowTitle({dimmed: dimmed, children: children}) {
4864
5480
  });
4865
5481
  }
4866
5482
 
4867
- function RowTerminalActions({actions: actions, activity: activity, instanceId: instanceId}) {
5483
+ function RowTerminalActions({actions: actions, activity: activity, instanceId: instanceId, surface: surface}) {
4868
5484
  const lock = useActionClusterState(), [only, ...rest] = actions;
4869
5485
  return only ? /* @__PURE__ */ jsxRuntime.jsx(ActionClusterProvider, {
4870
5486
  value: lock,
@@ -4876,14 +5492,16 @@ function RowTerminalActions({actions: actions, activity: activity, instanceId: i
4876
5492
  inButtonCard: !0
4877
5493
  },
4878
5494
  instanceId: instanceId,
4879
- surface: "inline-row"
5495
+ placement: "inline-row",
5496
+ surface: surface
4880
5497
  }) : /* @__PURE__ */ jsxRuntime.jsx(ActionsMenuButton, {
4881
5498
  actions: actions,
4882
5499
  activity: activity,
4883
5500
  inButtonCard: !0,
4884
5501
  instanceId: instanceId,
4885
5502
  label: "Select action",
4886
- surface: "inline-row"
5503
+ placement: "inline-row",
5504
+ surface: surface
4887
5505
  })
4888
5506
  }) : null;
4889
5507
  }
@@ -4904,7 +5522,7 @@ function TitleTag({hint: hint, tone: tone, children: children}) {
4904
5522
  });
4905
5523
  }
4906
5524
 
4907
- function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeIds, assignState: assignState, dateControl: dateControl, instanceId: instanceId, onOpen: onOpen, terminalActions: terminalActions = []}) {
5525
+ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeIds, assignState: assignState, dateState: dateState, dueDates: dueDates, instanceId: instanceId, onOpen: onOpen, surface: surface, terminalActions: terminalActions = []}) {
4908
5526
  const settled = workflowEngine.isTerminalActivityStatus(face.status), dimmed = settledDim(settled);
4909
5527
  /* @__PURE__ */
4910
5528
  return jsxRuntime.jsxs(WorkRow, {
@@ -4912,12 +5530,20 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeI
4912
5530
  children: [ settled || terminalActions.length === 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(RowTerminalActions, {
4913
5531
  actions: terminalActions,
4914
5532
  activity: face.activityName,
4915
- instanceId: instanceId
4916
- }), dateControl,
5533
+ instanceId: instanceId,
5534
+ surface: surface
5535
+ }),
5536
+ /* @__PURE__ */ jsxRuntime.jsx(ActivityDateControl, {
5537
+ dueDates: dueDates,
5538
+ instanceId: instanceId,
5539
+ state: dateState,
5540
+ surface: surface
5541
+ }),
4917
5542
  /* @__PURE__ */ jsxRuntime.jsx(AssignActivityControl, {
4918
5543
  assigneeIds: assigneeIds,
4919
5544
  instanceId: instanceId,
4920
- state: assignState
5545
+ state: assignState,
5546
+ surface: surface
4921
5547
  }) ]
4922
5548
  }),
4923
5549
  lead: /* @__PURE__ */ jsxRuntime.jsx(ActivityStatusLead, {
@@ -4942,7 +5568,7 @@ function ActivityRow({face: face, breadcrumb: breadcrumb, assigneeIds: assigneeI
4942
5568
  });
4943
5569
  }
4944
5570
 
4945
- function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity}) {
5571
+ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
4946
5572
  const activities = (entry.evaluation?.currentStage.activities ?? []).filter(t => !t.scopedOut), activityState = stateByActivity(entry.instance);
4947
5573
  return activities.length === 0 ?
4948
5574
  /* @__PURE__ */ jsxRuntime.jsx(ui.Box, {
@@ -4962,7 +5588,8 @@ function ActivitiesList({entry: entry, onOpenActivity: onOpenActivity}) {
4962
5588
  editableFields: entry.evaluation?.editableFields
4963
5589
  }),
4964
5590
  instanceId: entry.instance._id,
4965
- onOpen: () => onOpenActivity(t.activity.name)
5591
+ onOpen: () => onOpenActivity(t.activity.name),
5592
+ surface: surface
4966
5593
  }, t.activity.name))
4967
5594
  });
4968
5595
  }
@@ -5026,7 +5653,9 @@ const EDITOR_BY_KIND = {
5026
5653
  assignees: "assignees",
5027
5654
  boolean: "scalar",
5028
5655
  date: "scalar",
5656
+ dueDate: "scalar",
5029
5657
  datetime: "scalar",
5658
+ dueDatetime: "scalar",
5030
5659
  number: "scalar",
5031
5660
  progress: "scalar",
5032
5661
  string: "scalar",
@@ -5036,7 +5665,9 @@ const EDITOR_BY_KIND = {
5036
5665
  array: e => e.value.length > 0 ? pluralize__default.default("item", e.value.length, !0) : void 0,
5037
5666
  boolean: e => e.value === null ? void 0 : formatBoolean(e.value),
5038
5667
  date: e => e.value === null ? void 0 : formatDate(e.value),
5668
+ dueDate: e => e.value === null ? void 0 : formatDate(e.value),
5039
5669
  datetime: e => e.value === null ? void 0 : formatDateTime(e.value),
5670
+ dueDatetime: e => e.value === null ? void 0 : formatDateTime(e.value),
5040
5671
  "doc.refs": e => e.value.length > 0 ? pluralize__default.default("document", e.value.length, !0) : void 0,
5041
5672
  object: e => e.value === null ? void 0 : "Set",
5042
5673
  "release.ref": e => e.value === null ? void 0 : e.value.releaseName
@@ -5160,7 +5791,7 @@ function TodoFieldDialog({entry: entry, scope: scope, field: field, title: title
5160
5791
 
5161
5792
  const MAX_VISIBLE_PILLS = 4;
5162
5793
 
5163
- function FieldPills({entry: entry, scope: scope, definition: definition}) {
5794
+ function FieldPills({entry: entry, scope: scope, definition: definition, surface: surface}) {
5164
5795
  const [expanded, setExpanded] = react.useState(!1), pills = deriveFieldPills({
5165
5796
  scope: scope,
5166
5797
  definition: definition,
@@ -5178,7 +5809,8 @@ function FieldPills({entry: entry, scope: scope, definition: definition}) {
5178
5809
  children: [ visible.map(pill => /* @__PURE__ */ jsxRuntime.jsx(FieldPill, {
5179
5810
  entry: entry,
5180
5811
  pill: pill,
5181
- scope: scope
5812
+ scope: scope,
5813
+ surface: surface
5182
5814
  }, pill.name)), overflow > 0 ?
5183
5815
  /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
5184
5816
  mode: "bleed",
@@ -5212,7 +5844,9 @@ function isEmptyFace(face) {
5212
5844
 
5213
5845
  const KIND_ICON = {
5214
5846
  date: Calendar.CalendarIcon,
5847
+ dueDate: Calendar.CalendarIcon,
5215
5848
  datetime: Calendar.CalendarIcon,
5849
+ dueDatetime: Calendar.CalendarIcon,
5216
5850
  "doc.ref": Document.DocumentIcon,
5217
5851
  "doc.refs": Document.DocumentIcon,
5218
5852
  subject: Document.DocumentIcon
@@ -5364,18 +5998,20 @@ function PillButton({children: children, pill: pill, hintDisabled: hintDisabled
5364
5998
  });
5365
5999
  }
5366
6000
 
5367
- function useFieldCommit({entry: entry, editability: editability}) {
5368
- const {editFieldFor: editFieldFor} = useWorkflowContext(), target = workflowReact.editFieldTarget(editability);
6001
+ function useFieldCommit({entry: entry, editability: editability, surface: surface}) {
6002
+ const editField = useEditField(surface), instanceId = entry.instance._id;
5369
6003
  return {
5370
- set: value => editFieldFor(entry.instance._id, {
5371
- target: target,
6004
+ set: value => editField({
6005
+ instanceId: instanceId,
6006
+ field: editability,
5372
6007
  mode: "set",
5373
6008
  value: value
5374
- }).then(() => {}),
5375
- unset: () => editFieldFor(entry.instance._id, {
5376
- target: target,
6009
+ }),
6010
+ unset: () => editField({
6011
+ instanceId: instanceId,
6012
+ field: editability,
5377
6013
  mode: "unset"
5378
- }).then(() => {})
6014
+ })
5379
6015
  };
5380
6016
  }
5381
6017
 
@@ -5445,11 +6081,28 @@ function ReadOnlyPill({pill: pill}) {
5445
6081
  });
5446
6082
  }
5447
6083
 
5448
- function ScalarPill({entry: entry, pill: pill, editability: editability}) {
5449
- const [open, setOpen] = react.useState(!1), {set: set} = useFieldCommit({
6084
+ function usePillWrite(args) {
6085
+ const {entry: entry, editability: editability, surface: surface, onSaved: onSaved} = args, commit = useFieldCommit({
6086
+ entry: entry,
6087
+ editability: editability,
6088
+ surface: surface
6089
+ }), saver = useSaveField({
6090
+ instanceId: entry.instance._id,
6091
+ ...onSaved === void 0 ? {} : {
6092
+ onSaved: onSaved
6093
+ }
6094
+ });
6095
+ return {
6096
+ ...commit,
6097
+ ...saver
6098
+ };
6099
+ }
6100
+
6101
+ function ScalarPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6102
+ const [open, setOpen] = react.useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
5450
6103
  entry: entry,
5451
- editability: editability
5452
- }), {saving: saving, save: save} = useSaveField({
6104
+ editability: editability,
6105
+ surface: surface,
5453
6106
  onSaved: () => setOpen(!1)
5454
6107
  });
5455
6108
  /* @__PURE__ */
@@ -5475,18 +6128,21 @@ function ScalarPill({entry: entry, pill: pill, editability: editability}) {
5475
6128
  });
5476
6129
  }
5477
6130
 
5478
- function DatePill({entry: entry, pill: pill, editability: editability, kind: kind}) {
6131
+ function DatePill({entry: entry, pill: pill, editability: editability, surface: surface, kind: kind}) {
5479
6132
  const [open, setOpen] = react.useState(!1), {set: set} = useFieldCommit({
5480
6133
  entry: entry,
5481
- editability: editability
5482
- }), {save: save} = useSaveField();
6134
+ editability: editability,
6135
+ surface: surface
6136
+ }), {save: save} = useSaveField({
6137
+ instanceId: entry.instance._id
6138
+ });
5483
6139
  /* @__PURE__ */
5484
6140
  return jsxRuntime.jsx(PillPopover, {
5485
6141
  content: /* @__PURE__ */ jsxRuntime.jsx(workflowComponents.DatePicker, {
5486
6142
  onSelect: next => {
5487
- kind === "date" && setOpen(!1), save(() => set(serializeDateFieldValue(next, kind)));
6143
+ hasTimeOfDay(kind) || setOpen(!1), save(() => set(serializeDateFieldValue(next, kind)));
5488
6144
  },
5489
- selectTime: kind === "datetime",
6145
+ selectTime: hasTimeOfDay(kind),
5490
6146
  value: parseDateFieldValue(pill.entry?.value, kind)
5491
6147
  }),
5492
6148
  onDismiss: () => setOpen(!1),
@@ -5496,11 +6152,12 @@ function DatePill({entry: entry, pill: pill, editability: editability, kind: kin
5496
6152
  });
5497
6153
  }
5498
6154
 
5499
- function BooleanPill({entry: entry, pill: pill, editability: editability}) {
5500
- const {set: set} = useFieldCommit({
6155
+ function BooleanPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6156
+ const {set: set, save: save} = usePillWrite({
5501
6157
  entry: entry,
5502
- editability: editability
5503
- }), {save: save} = useSaveField(), chrome = usePillChrome(), current = pill.entry?._type === "boolean" ? pill.entry.value : null, menuId = react.useId();
6158
+ editability: editability,
6159
+ surface: surface
6160
+ }), chrome = usePillChrome(), current = pill.entry?._type === "boolean" ? pill.entry.value : null, menuId = react.useId();
5504
6161
  /* @__PURE__ */
5505
6162
  return jsxRuntime.jsx(PillHint, {
5506
6163
  pill: pill,
@@ -5544,12 +6201,14 @@ function BooleanPill({entry: entry, pill: pill, editability: editability}) {
5544
6201
  });
5545
6202
  }
5546
6203
 
5547
- function ActorPill({entry: entry, pill: pill, editability: editability}) {
6204
+ function ActorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5548
6205
  const [open, setOpen] = react.useState(!1), {set: set, unset: unset} = useFieldCommit({
5549
6206
  entry: entry,
5550
- editability: editability
6207
+ editability: editability,
6208
+ surface: surface
5551
6209
  }), current = isActorShape(pill.entry?.value) ? pill.entry.value : null, {pick: pick} = useActorPick({
5552
6210
  current: current,
6211
+ instanceId: entry.instance._id,
5553
6212
  onSave: set,
5554
6213
  onUnset: unset
5555
6214
  });
@@ -5569,12 +6228,14 @@ function ActorPill({entry: entry, pill: pill, editability: editability}) {
5569
6228
  });
5570
6229
  }
5571
6230
 
5572
- function AssigneePill({entry: entry, pill: pill, editability: editability}) {
6231
+ function AssigneePill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5573
6232
  const [open, setOpen] = react.useState(!1), {set: set, unset: unset} = useFieldCommit({
5574
6233
  entry: entry,
5575
- editability: editability
6234
+ editability: editability,
6235
+ surface: surface
5576
6236
  }), current = pill.entry?._type === "assignee" ? pill.entry.value : null, {pick: pick} = useAssigneePick({
5577
6237
  current: current,
6238
+ instanceId: entry.instance._id,
5578
6239
  onSave: set,
5579
6240
  onUnset: unset
5580
6241
  });
@@ -5594,11 +6255,12 @@ function AssigneePill({entry: entry, pill: pill, editability: editability}) {
5594
6255
  });
5595
6256
  }
5596
6257
 
5597
- function AssigneesPill({entry: entry, pill: pill, editability: editability}) {
5598
- const [open, setOpen] = react.useState(!1), {set: set} = useFieldCommit({
6258
+ function AssigneesPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
6259
+ const [open, setOpen] = react.useState(!1), {set: set, saving: saving, save: save} = usePillWrite({
5599
6260
  entry: entry,
5600
- editability: editability
5601
- }), {saving: saving, save: save} = useSaveField(), current = pill.face.kind === "assignees" ? pill.face.assignees : [];
6261
+ editability: editability,
6262
+ surface: surface
6263
+ }), current = pill.face.kind === "assignees" ? pill.face.assignees : [];
5602
6264
  /* @__PURE__ */
5603
6265
  return jsxRuntime.jsx(PillPopover, {
5604
6266
  content: /* @__PURE__ */ jsxRuntime.jsx(AssigneePicker, {
@@ -5641,46 +6303,53 @@ function TodoPill({entry: entry, pill: pill, scope: scope}) {
5641
6303
  });
5642
6304
  }
5643
6305
 
5644
- function ScalarEditorPill({entry: entry, pill: pill, editability: editability}) {
6306
+ function ScalarEditorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5645
6307
  return editability.type === "boolean" ? /* @__PURE__ */ jsxRuntime.jsx(BooleanPill, {
5646
6308
  editability: editability,
5647
6309
  entry: entry,
5648
- pill: pill
5649
- }) : editability.type === "date" || editability.type === "datetime" ? /* @__PURE__ */ jsxRuntime.jsx(DatePill, {
6310
+ pill: pill,
6311
+ surface: surface
6312
+ }) : isDateFieldKind(editability.type) ? /* @__PURE__ */ jsxRuntime.jsx(DatePill, {
5650
6313
  editability: editability,
5651
6314
  entry: entry,
5652
6315
  kind: editability.type,
5653
- pill: pill
6316
+ pill: pill,
6317
+ surface: surface
5654
6318
  }) : /* @__PURE__ */ jsxRuntime.jsx(ScalarPill, {
5655
6319
  editability: editability,
5656
6320
  entry: entry,
5657
- pill: pill
6321
+ pill: pill,
6322
+ surface: surface
5658
6323
  });
5659
6324
  }
5660
6325
 
5661
- function EditorPill({entry: entry, pill: pill, editability: editability}) {
6326
+ function EditorPill({entry: entry, pill: pill, editability: editability, surface: surface}) {
5662
6327
  return pill.editor === "scalar" ? /* @__PURE__ */ jsxRuntime.jsx(ScalarEditorPill, {
5663
6328
  editability: editability,
5664
6329
  entry: entry,
5665
- pill: pill
6330
+ pill: pill,
6331
+ surface: surface
5666
6332
  }) : pill.editor === "actor" ? /* @__PURE__ */ jsxRuntime.jsx(ActorPill, {
5667
6333
  editability: editability,
5668
6334
  entry: entry,
5669
- pill: pill
6335
+ pill: pill,
6336
+ surface: surface
5670
6337
  }) : pill.editor === "assignee" ? /* @__PURE__ */ jsxRuntime.jsx(AssigneePill, {
5671
6338
  editability: editability,
5672
6339
  entry: entry,
5673
- pill: pill
6340
+ pill: pill,
6341
+ surface: surface
5674
6342
  }) : pill.editor === "assignees" ? /* @__PURE__ */ jsxRuntime.jsx(AssigneesPill, {
5675
6343
  editability: editability,
5676
6344
  entry: entry,
5677
- pill: pill
6345
+ pill: pill,
6346
+ surface: surface
5678
6347
  }) : /* @__PURE__ */ jsxRuntime.jsx(ReadOnlyPill, {
5679
6348
  pill: pill
5680
6349
  });
5681
6350
  }
5682
6351
 
5683
- function FieldPill({entry: entry, pill: pill, scope: scope}) {
6352
+ function FieldPill({entry: entry, pill: pill, scope: scope, surface: surface}) {
5684
6353
  if (pill.editor === "todoList" && pill.entry !== void 0) /* @__PURE__ */
5685
6354
  return jsxRuntime.jsx(TodoPill, {
5686
6355
  entry: entry,
@@ -5693,7 +6362,8 @@ function FieldPill({entry: entry, pill: pill, scope: scope}) {
5693
6362
  }) : /* @__PURE__ */ jsxRuntime.jsx(EditorPill, {
5694
6363
  editability: editability,
5695
6364
  entry: entry,
5696
- pill: pill
6365
+ pill: pill,
6366
+ surface: surface
5697
6367
  });
5698
6368
  }
5699
6369
 
@@ -5824,7 +6494,7 @@ function InstanceToolLink({entry: entry}) {
5824
6494
  }
5825
6495
  });
5826
6496
  }, [ instanceId ]);
5827
- return tools.some(tool => tool.name === WORKFLOWS_TOOL_NAME) ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
6497
+ return workflowsToolAvailable(tools) ? /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
5828
6498
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
5829
6499
  as: WorkflowIntentLink,
5830
6500
  fontSize: 1,
@@ -6027,6 +6697,7 @@ function StageGlyph(props) {
6027
6697
  }
6028
6698
 
6029
6699
  function StageFace({completed: completed = !1, detail: detail, title: title}) {
6700
+ const tone = completed ? "default" : "primary";
6030
6701
  /* @__PURE__ */
6031
6702
  return jsxRuntime.jsxs(ui.Flex, {
6032
6703
  align: "center",
@@ -6035,8 +6706,8 @@ function StageFace({completed: completed = !1, detail: detail, title: title}) {
6035
6706
  children: [
6036
6707
  /* @__PURE__ */ jsxRuntime.jsx(sanity.TextWithTone, {
6037
6708
  size: 1,
6038
- tone: "primary",
6039
- children: completed ? /* @__PURE__ */ jsxRuntime.jsx(CheckmarkCircle.CheckmarkCircleIcon, {
6709
+ tone: tone,
6710
+ children: completed ? /* @__PURE__ */ jsxRuntime.jsx(CheckmarkCircleFilledIcon, {
6040
6711
  style: {
6041
6712
  color: "inherit"
6042
6713
  }
@@ -6048,7 +6719,7 @@ function StageFace({completed: completed = !1, detail: detail, title: title}) {
6048
6719
  }),
6049
6720
  /* @__PURE__ */ jsxRuntime.jsx(sanity.TextWithTone, {
6050
6721
  size: 1,
6051
- tone: "primary",
6722
+ tone: tone,
6052
6723
  weight: "medium",
6053
6724
  children: title
6054
6725
  }), detail === void 0 ? null : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
@@ -6066,10 +6737,10 @@ function StageChip({completed: completed = !1, detail: detail, interactive: inte
6066
6737
  padding: 3,
6067
6738
  radius: 4,
6068
6739
  style: {
6069
- boxShadow: `inset 0 0 0 1px ${color.focusRing}`,
6740
+ boxShadow: `inset 0 0 0 1px ${completed ? color.badge.default.fg : color.focusRing}`,
6070
6741
  cursor: interactive ? "help" : "default"
6071
6742
  },
6072
- tone: "primary",
6743
+ tone: completed ? "default" : "primary",
6073
6744
  ...interactive ? {
6074
6745
  tabIndex: 0
6075
6746
  } : {},
@@ -6297,7 +6968,8 @@ function WorkflowInstanceSection({defaultOpen: defaultOpen, entry: entry, onOpen
6297
6968
  children: [
6298
6969
  /* @__PURE__ */ jsxRuntime.jsx(InstanceSnapshotBody, {
6299
6970
  entry: entry,
6300
- onOpenActivity: onOpenActivity
6971
+ onOpenActivity: onOpenActivity,
6972
+ surface: "document-view"
6301
6973
  }),
6302
6974
  /* @__PURE__ */ jsxRuntime.jsx(InstanceToolLink, {
6303
6975
  entry: entry
@@ -6306,7 +6978,7 @@ function WorkflowInstanceSection({defaultOpen: defaultOpen, entry: entry, onOpen
6306
6978
  });
6307
6979
  }
6308
6980
 
6309
- function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
6981
+ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
6310
6982
  const definition = useDefinition(entry), {instance: instance} = entry;
6311
6983
  /* @__PURE__ */
6312
6984
  return jsxRuntime.jsxs(ui.Stack, {
@@ -6317,7 +6989,8 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
6317
6989
  /* @__PURE__ */ jsxRuntime.jsx(FieldPills, {
6318
6990
  definition: definition,
6319
6991
  entry: entry,
6320
- scope: "workflow"
6992
+ scope: "workflow",
6993
+ surface: surface
6321
6994
  }),
6322
6995
  /* @__PURE__ */ jsxRuntime.jsxs(ui.Card, {
6323
6996
  border: !0,
@@ -6342,14 +7015,15 @@ function InstanceSnapshotBody({entry: entry, onOpenActivity: onOpenActivity}) {
6342
7015
  padding: 2,
6343
7016
  children: /* @__PURE__ */ jsxRuntime.jsx(StageSection, {
6344
7017
  entry: entry,
6345
- onOpenActivity: onOpenActivity
7018
+ onOpenActivity: onOpenActivity,
7019
+ surface: surface
6346
7020
  })
6347
7021
  }) ]
6348
7022
  }) ]
6349
7023
  });
6350
7024
  }
6351
7025
 
6352
- function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
7026
+ function StageSection({entry: entry, onOpenActivity: onOpenActivity, surface: surface}) {
6353
7027
  const definition = useDefinition(entry), notice = instanceNotice(entry);
6354
7028
  return notice !== null ? notice : /* @__PURE__ */ jsxRuntime.jsx(StaleLock, {
6355
7029
  stale: isEvaluationStale(entry),
@@ -6359,11 +7033,13 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
6359
7033
  /* @__PURE__ */ jsxRuntime.jsx(FieldPills, {
6360
7034
  definition: definition,
6361
7035
  entry: entry,
6362
- scope: "stage"
7036
+ scope: "stage",
7037
+ surface: surface
6363
7038
  }),
6364
7039
  /* @__PURE__ */ jsxRuntime.jsx(ActivitiesList, {
6365
7040
  entry: entry,
6366
- onOpenActivity: onOpenActivity
7041
+ onOpenActivity: onOpenActivity,
7042
+ surface: surface
6367
7043
  }) ]
6368
7044
  })
6369
7045
  });
@@ -6372,8 +7048,9 @@ function StageSection({entry: entry, onOpenActivity: onOpenActivity}) {
6372
7048
  function CautionNote({action: action, label: label}) {
6373
7049
  /* @__PURE__ */
6374
7050
  return jsxRuntime.jsxs(ui.Card, {
6375
- padding: 2,
6376
7051
  paddingLeft: 3,
7052
+ paddingRight: 2,
7053
+ paddingY: 3,
6377
7054
  radius: 3,
6378
7055
  style: {
6379
7056
  alignItems: "center",
@@ -6401,18 +7078,20 @@ function CautionNote({action: action, label: label}) {
6401
7078
  }
6402
7079
 
6403
7080
  function CopyDetailsButton({unreadable: unreadable}) {
6404
- const toast = useClosableToast();
7081
+ const toast = useWorkflowToast();
6405
7082
  /* @__PURE__ */
6406
7083
  return jsxRuntime.jsx(ui.Button, {
6407
7084
  fontSize: 1,
6408
7085
  mode: "ghost",
6409
7086
  onClick: () => {
6410
7087
  navigator.clipboard.writeText(unreadableDocsReport(unreadable)).then(() => toast.push({
6411
- status: "success",
6412
- title: "Details copied"
7088
+ id: TOAST_ID.detailsCopy,
7089
+ status: "info",
7090
+ title: "Details copied to clipboard"
6413
7091
  }), () => toast.push({
7092
+ id: TOAST_ID.detailsCopy,
6414
7093
  status: "error",
6415
- title: "Could not copy the details"
7094
+ title: "Failed to copy the details"
6416
7095
  }));
6417
7096
  },
6418
7097
  padding: 2,
@@ -6438,7 +7117,7 @@ const HEADLINES = {
6438
7117
  };
6439
7118
 
6440
7119
  function remediation(invalid) {
6441
- return invalid.reason === "model-ahead" ? `"${invalid.documentId}" was created by a newer version of the workflow engine than this Studio can read. It needs a plugin update that supports the newer data model — share the details with your Studio maintainers.` : `This version of the Studio can't read "${invalid.documentId}" — it was likely created by a newer or different version of the workflow tooling. Share the details with your Studio maintainers.`;
7120
+ return invalid.reason === "model-ahead" ? `"${invalid.documentId}" was created by a newer version of the workflow engine than this Studio can read. It needs a plugin update that supports the newer data model — share the details with your Studio maintainers.` : `This version of the Studio cant read "${invalid.documentId}" — it was likely created by a newer or different version of the workflow tooling. Share the details with your Studio maintainers.`;
6442
7121
  }
6443
7122
 
6444
7123
  function InvalidDocNotice({invalid: invalid}) {
@@ -6489,18 +7168,6 @@ function TabSwitch({ariaControls: ariaControls, idPrefix: idPrefix, options: opt
6489
7168
  });
6490
7169
  }
6491
7170
 
6492
- function useDelayedFlag(active, delayMs) {
6493
- const [held, setHeld] = react.useState(!1);
6494
- return react.useEffect(() => {
6495
- if (!active) {
6496
- setHeld(!1);
6497
- return;
6498
- }
6499
- const timer = setTimeout(() => setHeld(!0), delayMs);
6500
- return () => clearTimeout(timer);
6501
- }, [ active, delayMs ]), held && active;
6502
- }
6503
-
6504
7171
  function openActivityGone(args) {
6505
7172
  const {entry: entry, target: target} = args;
6506
7173
  if (entry === void 0 || entry.instance.currentStage !== target.stage) return !0;
@@ -6565,18 +7232,16 @@ function coveredFieldNames(ctx) {
6565
7232
  return release && names.add(release.name), names;
6566
7233
  }
6567
7234
 
6568
- const OPTIONAL_INIT_KINDS = /* @__PURE__ */ new Set([ "array", "assignees", "doc.refs" ]);
6569
-
6570
7235
  function inputInitFields(args) {
6571
7236
  return (args.definition?.fields ?? []).filter(entry => entry.initialValue?.type === "input" && !args.covered.has(entry.name));
6572
7237
  }
6573
7238
 
6574
- function gatesStart(entry) {
6575
- return !OPTIONAL_INIT_KINDS.has(entry.type);
7239
+ function isRequiredEntry(entry) {
7240
+ return entry.required === !0;
6576
7241
  }
6577
7242
 
6578
7243
  function missingInitFields(initFields, values) {
6579
- return initFields.filter(entry => gatesStart(entry) && !isFieldFilled(entry, values[entry.name] ?? null));
7244
+ return initFields.filter(entry => isRequiredEntry(entry) && !isFieldFilled(entry, values[entry.name] ?? null));
6580
7245
  }
6581
7246
 
6582
7247
  function numericFilled(value) {
@@ -6633,7 +7298,9 @@ const asInitial = candidate => candidate, scalarInitialValue = (entry, value) =>
6633
7298
  value: value === !0
6634
7299
  }),
6635
7300
  date: scalarInitialValue,
7301
+ dueDate: scalarInitialValue,
6636
7302
  datetime: scalarInitialValue,
7303
+ dueDatetime: scalarInitialValue,
6637
7304
  "doc.ref": singleRefInitialValue,
6638
7305
  "doc.refs": (entry, value) => asInitial({
6639
7306
  type: entry.type,
@@ -6784,15 +7451,36 @@ function classifyStartError(err) {
6784
7451
  };
6785
7452
  }
6786
7453
 
7454
+ function startOutcomeToast(outcome, label) {
7455
+ return outcome.kind === "started-not-settled" ? {
7456
+ id: TOAST_ID.start,
7457
+ status: "warning",
7458
+ title: `“${label}” started but didn’t finish`,
7459
+ description: outcome.description
7460
+ } : outcome.kind === "not-allowed" ? {
7461
+ id: TOAST_ID.start,
7462
+ status: "warning",
7463
+ title: `“${label}” can’t be started right now`,
7464
+ description: outcome.description
7465
+ } : {
7466
+ id: TOAST_ID.start,
7467
+ status: "error",
7468
+ title: `Failed to start “${label}”`,
7469
+ description: outcome.description
7470
+ };
7471
+ }
7472
+
6787
7473
  function boundReleaseId(mapping, selectedReleaseId) {
6788
7474
  return mapping?.perspectiveField !== void 0 && selectedReleaseId !== void 0 ? selectedReleaseId : void 0;
6789
7475
  }
6790
7476
 
7477
+ const MAPPING_BROKEN_HINT = docTypeLabel => `Unavailable — this workflow isn’t set up correctly for ${docTypeLabel} documents`;
7478
+
6791
7479
  function startGate(args) {
6792
7480
  const {mapping: mapping, mappingIssue: mappingIssue, selectedReleaseId: selectedReleaseId, releaseActive: releaseActive} = args;
6793
7481
  if (mappingIssue !== void 0) return {
6794
7482
  blocked: !0,
6795
- tooltip: mappingIssue
7483
+ tooltip: MAPPING_BROKEN_HINT(args.docTypeLabel)
6796
7484
  };
6797
7485
  if (mapping.perspectiveField?.required === !0 && selectedReleaseId === void 0) return {
6798
7486
  blocked: !0,
@@ -6804,7 +7492,7 @@ function startGate(args) {
6804
7492
  };
6805
7493
  if (args.startFilterFailed === !0) return {
6806
7494
  blocked: !0,
6807
- tooltip: `${mapping.label} can't start for this document right now — the workflow's start condition isn't met`
7495
+ tooltip: `${mapping.label} cant start for this document right now — the workflows start condition isnt met`
6808
7496
  };
6809
7497
  if (args.unmetRequirement !== void 0) {
6810
7498
  const requirement = args.unmetRequirement;
@@ -7066,7 +7754,7 @@ function showRowOnError(key, mapping) {
7066
7754
  }
7067
7755
 
7068
7756
  function useStartWorkflow(args) {
7069
- const {mapping: mapping, docId: docId, initialValue: initialValue, source: source} = args, toast = useClosableToast(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = workflowStudio.useStudioObserver({
7757
+ const {mapping: mapping, docId: docId, initialValue: initialValue, source: source} = args, toast = useWorkflowToast(), schema = sanity.useSchema(), {engine: engine, openStartDialog: openStartDialog, mappingIssues: mappingIssues2, mappingStarts: mappingStarts} = useWorkflowContext(), observer = workflowStudio.useStudioObserver({
7070
7758
  engine: engine
7071
7759
  }), [starting, setStarting] = react.useState(!1), {selectedReleaseId: selectedReleaseId} = sanity.usePerspective(), releaseActive = sanity.useIsReleaseActive(), startBlock = mappingStarts.get(mappingKey(mapping)), hidden = workflowEngine.startKindOf({
7072
7760
  start: startBlock
@@ -7086,6 +7774,7 @@ function useStartWorkflow(args) {
7086
7774
  }), {blocked: blocked, tooltip: tooltip} = startGate({
7087
7775
  mapping: mapping,
7088
7776
  mappingIssue: mappingIssues2.get(mappingKey(mapping)),
7777
+ docTypeLabel: schema.get(mapping.docType)?.title ?? mapping.docType,
7089
7778
  selectedReleaseId: selectedReleaseId,
7090
7779
  releaseActive: releaseActive,
7091
7780
  startFilterFailed: filterFailed,
@@ -7109,8 +7798,9 @@ function useStartWorkflow(args) {
7109
7798
  selectedReleaseId: selectedReleaseId,
7110
7799
  ensureDocumentExists: observer.ensureDocumentExists,
7111
7800
  onPersistFailure: err => toast.push({
7801
+ id: TOAST_ID.documentSave,
7112
7802
  status: "error",
7113
- title: "Could not persist the document before starting",
7803
+ title: "Failed to save the document",
7114
7804
  description: describeError(err)
7115
7805
  }),
7116
7806
  openStartDialog: request => openStartDialog({
@@ -7251,7 +7941,8 @@ function ForMeList({entry: entry, work: work, identity: identity, onOpenActivity
7251
7941
  }),
7252
7942
  breadcrumb: breadcrumb,
7253
7943
  instanceId: instance._id,
7254
- onOpen: () => onOpenActivity(activityEval.activity.name)
7944
+ onOpen: () => onOpenActivity(activityEval.activity.name),
7945
+ surface: "document-view"
7255
7946
  }, activityEval.activity.name)), itemCount > 0 ? /* @__PURE__ */ jsxRuntime.jsx(TodoItemsList, {
7256
7947
  breadcrumb: breadcrumb,
7257
7948
  entry: entry,
@@ -7427,7 +8118,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7427
8118
  children: /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
7428
8119
  direction: "column",
7429
8120
  flex: 1,
7430
- children: view === "overview" ? /* @__PURE__ */ jsxRuntime.jsx(OverviewTab, {
8121
+ children: view === "overview" ? /* @__PURE__ */ jsxRuntime.jsx(OverviewSections, {
7431
8122
  active: active,
7432
8123
  empty: /* @__PURE__ */ jsxRuntime.jsx(OverviewEmptyState, {
7433
8124
  docId: documentId,
@@ -7451,7 +8142,7 @@ function WorkflowsPanel({entries: entries, documentId: documentId, docTypeMappin
7451
8142
  });
7452
8143
  }
7453
8144
 
7454
- function OverviewTab({active: active, empty: empty, finished: finished, sectionFor: sectionFor}) {
8145
+ function OverviewSections({active: active, empty: empty, finished: finished, sectionFor: sectionFor}) {
7455
8146
  /* @__PURE__ */
7456
8147
  return jsxRuntime.jsxs(ui.Stack, {
7457
8148
  gap: 5,
@@ -7599,6 +8290,23 @@ function workflowIssue(args) {
7599
8290
  if (!workflowEngine.isInputSourced(subject)) return `autoStart: "${docType}" → workflow "${name}" has a self-filling subject (not caller-provided), so the document can't be its subject — skipped.`;
7600
8291
  }
7601
8292
 
8293
+ function createSubscribers() {
8294
+ const listeners = /* @__PURE__ */ new Set;
8295
+ return {
8296
+ subscribe(listener) {
8297
+ return listeners.add(listener), () => {
8298
+ listeners.delete(listener);
8299
+ };
8300
+ },
8301
+ notify() {
8302
+ for (const listener of [ ...listeners ]) listener();
8303
+ },
8304
+ clear() {
8305
+ listeners.clear();
8306
+ }
8307
+ };
8308
+ }
8309
+
7602
8310
  function actualType$(previews, bareId) {
7603
8311
  return previews.unstable_observeDocumentPairAvailability(bareId).pipe(operators.switchMap(({draft: draft, published: published}) => draft.available ? previews.observeDocumentTypeFromId(csm.getDraftId(bareId)) : published.available ? previews.observeDocumentTypeFromId(bareId) : draft.reason === "PERMISSION_DENIED" || published.reason === "PERMISSION_DENIED" ? rxjs.of(void 0) : previews.unstable_observeVersionDocumentIds(bareId).pipe(operators.switchMap(([firstVersionId]) => firstVersionId === void 0 ? rxjs.of(null) : previews.observeDocumentTypeFromId(firstVersionId)))), operators.distinctUntilChanged());
7604
8312
  }
@@ -7606,12 +8314,10 @@ function actualType$(previews, bareId) {
7606
8314
  const REBUILD_DELAY_MS = 5e3;
7607
8315
 
7608
8316
  function createActualTypeProbeStore(previews) {
7609
- const verdicts = /* @__PURE__ */ new Map, live = /* @__PURE__ */ new Map, listeners = /* @__PURE__ */ new Set, notify = () => {
7610
- for (const listener of [ ...listeners ]) listener();
7611
- }, start = (bareId, entry) => actualType$(previews, bareId).subscribe({
8317
+ const verdicts = /* @__PURE__ */ new Map, live = /* @__PURE__ */ new Map, subscribers = createSubscribers(), start = (bareId, entry) => actualType$(previews, bareId).subscribe({
7612
8318
  next: value => {
7613
8319
  verdicts.has(bareId) && verdicts.get(bareId) === value || (verdicts.set(bareId, value),
7614
- notify());
8320
+ subscribers.notify());
7615
8321
  },
7616
8322
  error: err => {
7617
8323
  console.error(`[workflow-studio-plugin] actual-type probe for "${bareId}" failed:`, err),
@@ -7621,12 +8327,14 @@ function createActualTypeProbeStore(previews) {
7621
8327
  }
7622
8328
  });
7623
8329
  return {
7624
- subscribe(listener) {
7625
- return listeners.add(listener), () => {
7626
- listeners.delete(listener);
7627
- };
7628
- },
8330
+ subscribe: subscribers.subscribe,
7629
8331
  read: bareId => verdicts.get(bareId),
8332
+ seed(types2) {
8333
+ let changed = !1;
8334
+ for (const [bareId, actualType] of types2) verdicts.has(bareId) || (verdicts.set(bareId, actualType),
8335
+ changed = !0);
8336
+ changed && subscribers.notify();
8337
+ },
7630
8338
  track(bareId) {
7631
8339
  let entry = live.get(bareId);
7632
8340
  if (entry === void 0) {
@@ -7647,10 +8355,6 @@ function createActualTypeProbeStore(previews) {
7647
8355
  };
7648
8356
  }
7649
8357
 
7650
- function contentDocumentTypes(schema) {
7651
- return schema.getTypeNames().filter(name => schema.get(name)?.type?.name === "document" && !WORKFLOW_SYSTEM_TYPES.includes(name));
7652
- }
7653
-
7654
8358
  async function readDeployedDefinitions(engine) {
7655
8359
  return (await engine.query({
7656
8360
  groq: workflowEngine.latestDefinitionsGroq()
@@ -7825,7 +8529,7 @@ const REPORT_FLUSH_MS = 50, EMPTY_COMMITTED = {
7825
8529
 
7826
8530
  function createEntriesStore() {
7827
8531
  let committed = EMPTY_COMMITTED, reports = /* @__PURE__ */ new Map, byInstance = /* @__PURE__ */ new Map, byDocument = /* @__PURE__ */ new Map;
7828
- const listeners = /* @__PURE__ */ new Set;
8532
+ const subscribers = createSubscribers();
7829
8533
  let flushTimer;
7830
8534
  const recompute = () => {
7831
8535
  flushTimer !== void 0 && (clearTimeout(flushTimer), flushTimer = void 0);
@@ -7838,10 +8542,8 @@ function createEntriesStore() {
7838
8542
  byInstance: nextByInstance,
7839
8543
  previous: byDocument
7840
8544
  });
7841
- if (!(nextByInstance === byInstance && nextByDocument === byDocument)) {
7842
- byInstance = nextByInstance, byDocument = nextByDocument;
7843
- for (const listener of [ ...listeners ]) listener();
7844
- }
8545
+ nextByInstance === byInstance && nextByDocument === byDocument || (byInstance = nextByInstance,
8546
+ byDocument = nextByDocument, subscribers.notify());
7845
8547
  };
7846
8548
  return {
7847
8549
  setCommitted(inputs) {
@@ -7857,13 +8559,9 @@ function createEntriesStore() {
7857
8559
  getReport: instanceId => reports.get(instanceId),
7858
8560
  getInstance: instanceId => byInstance.get(instanceId),
7859
8561
  getDocument: docId => byDocument.get(docId) ?? EMPTY_ENTRIES,
7860
- subscribe(listener) {
7861
- return listeners.add(listener), () => {
7862
- listeners.delete(listener);
7863
- };
7864
- },
8562
+ subscribe: subscribers.subscribe,
7865
8563
  dispose() {
7866
- flushTimer !== void 0 && clearTimeout(flushTimer), flushTimer = void 0, listeners.clear();
8564
+ flushTimer !== void 0 && clearTimeout(flushTimer), flushTimer = void 0, subscribers.clear();
7867
8565
  }
7868
8566
  };
7869
8567
  }
@@ -7994,7 +8692,7 @@ function WorkflowProvider(props) {
7994
8692
  try {
7995
8693
  await (drainRef.current?.(instanceId));
7996
8694
  } catch (err) {
7997
- throw new Error(`The change was committed, but running its effects failed: ${describeError(err)}`, {
8695
+ throw new EffectsIncompleteError(describeError(err), {
7998
8696
  cause: err
7999
8697
  });
8000
8698
  }
@@ -8125,12 +8823,19 @@ function WorkflowProvider(props) {
8125
8823
  });
8126
8824
  }
8127
8825
 
8128
- function sameEntries(a, b) {
8129
- if (a.size !== b.size) return !1;
8130
- for (const [key, value] of b) if (a.get(key) !== value) return !1;
8131
- return !0;
8826
+ function entriesSameBy(equal) {
8827
+ return (a, b) => {
8828
+ if (a.size !== b.size) return !1;
8829
+ for (const [key, value] of b) {
8830
+ const previous = a.get(key);
8831
+ if (previous === void 0 || !equal(previous, value)) return !1;
8832
+ }
8833
+ return !0;
8834
+ };
8132
8835
  }
8133
8836
 
8837
+ const sameIssues = entriesSameBy(sameMappingIssue), sameVersions = entriesSameBy(Object.is);
8838
+
8134
8839
  function sameMappings(a, b) {
8135
8840
  return a.length === b.length && a.every((mapping, index) => {
8136
8841
  const other = b[index];
@@ -8154,10 +8859,7 @@ function useDeployedDefinitions(args) {
8154
8859
  contentResource: contentResource,
8155
8860
  schemaContentTypes: schemaContentTypes
8156
8861
  });
8157
- for (const [rowKey, message] of found) {
8158
- const signature = `${rowKey}|${message}`;
8159
- logged.current.has(signature) || (logged.current.add(signature), console.error(`[workflow-studio-plugin] ${message}`));
8160
- }
8862
+ logOnce(mappingIssueLogLines(found), logged.current);
8161
8863
  const resolvedAutoStart = resolveAutoStart({
8162
8864
  autoStart: mappingAutoStartMap(effectiveMappings),
8163
8865
  knownDocTypes: new Set(schema.getTypeNames()),
@@ -8165,7 +8867,7 @@ function useDeployedDefinitions(args) {
8165
8867
  });
8166
8868
  if (logOnce(resolvedAutoStart.warnings, logged.current), cancelled) return;
8167
8869
  setMappings(prev => sameMappings(prev, effectiveMappings) ? prev : effectiveMappings),
8168
- setIssues(prev => sameEntries(prev, found) ? prev : found), setStarts(mappingStartBlocks({
8870
+ setIssues(prev => sameIssues(prev, found) ? prev : found), setStarts(mappingStartBlocks({
8169
8871
  mappings: effectiveMappings,
8170
8872
  definitions: definitions
8171
8873
  })), setFields(mappingDeclaredFields({
@@ -8173,7 +8875,7 @@ function useDeployedDefinitions(args) {
8173
8875
  definitions: definitions
8174
8876
  }));
8175
8877
  const versions = new Map(workflowEngine.latestDeployedDefinitions(definitions).map(d => [ d.name, d.version ]));
8176
- setLatestVersions(prev => sameEntries(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
8878
+ setLatestVersions(prev => sameVersions(prev, versions) ? prev : versions), setAutoStartByType(resolvedAutoStart.byType),
8177
8879
  setAutoStartDefinitions(resolvedAutoStart.definitions);
8178
8880
  })().catch(err => {
8179
8881
  console.error("[workflow-studio-plugin] mapping discovery failed — keeping the last resolved workflow state:", err);
@@ -8317,16 +9019,8 @@ function ChecklistInput({value: value, onChange: onChange}) {
8317
9019
  value: it.label
8318
9020
  })
8319
9021
  }),
8320
- /* @__PURE__ */ jsxRuntime.jsx(HoverHint, {
8321
- text: "Remove item",
8322
- children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, {
8323
- "aria-label": "Remove item",
8324
- fontSize: 1,
8325
- icon: Close.CloseIcon,
8326
- mode: "bleed",
8327
- onClick: () => remove(i),
8328
- padding: 2
8329
- })
9022
+ /* @__PURE__ */ jsxRuntime.jsx(RemoveItemButton, {
9023
+ onClick: () => remove(i)
8330
9024
  }) ]
8331
9025
  }, it._key)),
8332
9026
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
@@ -8430,7 +9124,7 @@ function useLatestDefinition(definitionName) {
8430
9124
  function useCreateDoc(args) {
8431
9125
  const {releaseId: releaseId, onCreated: onCreated} = args, {engine: engine, binding: binding} = useWorkflowContext(), observer = workflowStudio.useStudioObserver({
8432
9126
  engine: engine
8433
- }), schema = sanity.useSchema(), templates = sanity.useTemplates(), resolverContext = sanity.useInitialValueResolverContext(), toast = useClosableToast(), [creating, setCreating] = react.useState(!1);
9127
+ }), schema = sanity.useSchema(), templates = sanity.useTemplates(), resolverContext = sanity.useInitialValueResolverContext(), toast = useWorkflowToast(), [creating, setCreating] = react.useState(!1);
8434
9128
  return {
8435
9129
  creating: creating,
8436
9130
  createDoc: async type => {
@@ -8455,8 +9149,9 @@ function useCreateDoc(args) {
8455
9149
  }));
8456
9150
  } catch (err) {
8457
9151
  toast.push({
9152
+ id: TOAST_ID.documentCreate,
8458
9153
  status: "error",
8459
- title: "Could not create document",
9154
+ title: "Failed to create the document",
8460
9155
  description: describeError(err)
8461
9156
  });
8462
9157
  } finally {
@@ -8668,7 +9363,7 @@ function InitFieldRow({entry: entry, docType: docType, releaseId: releaseId, val
8668
9363
  /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
8669
9364
  size: 1,
8670
9365
  weight: "medium",
8671
- children: gatesStart(entry) ? `${label} (Required)` : label
9366
+ children: isRequiredEntry(entry) ? `${label} (Required)` : label
8672
9367
  }), entry.description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, {
8673
9368
  muted: !0,
8674
9369
  size: 1,
@@ -8773,31 +9468,12 @@ function ResumeNote({onStartNew: onStartNew}) {
8773
9468
 
8774
9469
  function handleStartError(args) {
8775
9470
  const {label: label, toast: toast, seedInstance: seedInstance, onClose: onClose} = args, outcome = classifyStartError(args.err);
8776
- if (outcome.kind === "started-not-settled") {
8777
- toast.push({
8778
- status: "warning",
8779
- title: `${label} started, but didn’t finish auto-advancing`,
8780
- description: outcome.description
8781
- }), outcome.instance !== void 0 && seedInstance(outcome.instance), onClose();
8782
- return;
8783
- }
8784
- if (outcome.kind === "not-allowed") {
8785
- toast.push({
8786
- status: "warning",
8787
- title: `${label} can't be started right now`,
8788
- description: outcome.description
8789
- });
8790
- return;
8791
- }
8792
- toast.push({
8793
- status: "error",
8794
- title: `Failed to start ${label}`,
8795
- description: outcome.description
8796
- });
9471
+ toast.push(startOutcomeToast(outcome, label)), outcome.kind === "started-not-settled" && (outcome.instance !== void 0 && seedInstance(outcome.instance),
9472
+ onClose());
8797
9473
  }
8798
9474
 
8799
9475
  function StartWorkflowForm({request: request, onClose: onClose}) {
8800
- const {definition: definitionName, label: label, mapping: mapping, subjectDocId: subjectDocId} = request, {engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), toast = useClosableToast(), {def: definition, error: definitionError} = useLatestDefinition(definitionName), {selectedReleaseId: selectedReleaseId} = sanity.usePerspective(), releaseId = boundReleaseId(mapping, selectedReleaseId), [values, setValues] = react.useState({}), [attempt, setAttempt] = react.useState(void 0), starting = attempt !== void 0, [heldInstanceId, setHeldInstanceId] = react.useState(void 0), [startNew, setStartNew] = react.useState(!1), {entries: entries} = useWorkflowsForDocument(subjectDocId), resumeEntry = presentedResumeEntry({
9476
+ const {definition: definitionName, label: label, mapping: mapping, subjectDocId: subjectDocId} = request, {engine: engine, binding: binding, seedInstance: seedInstance} = useWorkflowContext(), telemetry2 = workflowReact.useWorkflowTelemetry(), toast = useWorkflowToast(), {def: definition, error: definitionError} = useLatestDefinition(definitionName), {selectedReleaseId: selectedReleaseId} = sanity.usePerspective(), releaseId = boundReleaseId(mapping, selectedReleaseId), [values, setValues] = react.useState({}), [attempt, setAttempt] = react.useState(void 0), starting = attempt !== void 0, [heldInstanceId, setHeldInstanceId] = react.useState(void 0), [startNew, setStartNew] = react.useState(!1), {entries: entries} = useWorkflowsForDocument(subjectDocId), resumeEntry = presentedResumeEntry({
8801
9477
  entries: entries,
8802
9478
  definitionName: definitionName,
8803
9479
  heldInstanceId: heldInstanceId,
@@ -8837,8 +9513,9 @@ function StartWorkflowForm({request: request, onClose: onClose}) {
8837
9513
  values: values
8838
9514
  }));
8839
9515
  toast.push({
8840
- status: "success",
8841
- title: `${definition?.title ?? label} started`,
9516
+ id: TOAST_ID.start,
9517
+ status: "info",
9518
+ title: `“${definition?.title ?? label}” started`,
8842
9519
  description: `Stage: ${stageTitle(definition ?? void 0, result.instance.currentStage)}`
8843
9520
  }), seedInstance(result.instance), onClose();
8844
9521
  } catch (err) {
@@ -9340,6 +10017,22 @@ function finishedLinesOf(entries) {
9340
10017
  return [ ...latestByType.values() ].sort((a, b) => b.at - a.at).map(({entry: entry}) => entry);
9341
10018
  }
9342
10019
 
10020
+ function openStageTaskCount(evaluation) {
10021
+ if (evaluation !== void 0) return evaluation.currentStage.activities.filter(activity => isOpenActivityStatus(activity.status)).length;
10022
+ }
10023
+
10024
+ const OPEN_VIEW = "Open the Workflows view";
10025
+
10026
+ function unassignedLineHint(args) {
10027
+ return !args.identityKnown || args.openTaskCount === void 0 ? {
10028
+ text: OPEN_VIEW
10029
+ } : args.openTaskCount === 0 ? {
10030
+ text: "No open tasks in this stage"
10031
+ } : {
10032
+ text: `${pluralize__default.default("open task", args.openTaskCount, !0)}, none assigned to you`
10033
+ };
10034
+ }
10035
+
9343
10036
  function WorkflowFormStrip(props) {
9344
10037
  const {mappings: mappings} = props, {isDocResolved: isDocResolved, discoveryInvalid: discoveryInvalid} = useWorkflowContext(), {docId: docId, entries: entries} = useWorkflowsForDocument(props.value?._id ?? null), {params: params, setParams: setParams} = structure.usePaneRouter(), telemetry2 = workflowReact.useWorkflowTelemetry(), openWorkflowsView = focusId => {
9345
10038
  telemetry2.log(WorkflowFormStripClicked), setParams(focusedViewParams(params, focusId));
@@ -9384,18 +10077,17 @@ function StripLines({active: active, docId: docId, finished: finished, initialVa
9384
10077
  });
9385
10078
  }
9386
10079
 
9387
- function lineHint(identity, forMe) {
10080
+ function lineHint(entry, identity) {
10081
+ const forMe = forMeWorkOf([ entry ], identity);
9388
10082
  return forMe.work.some(({shown: shown}) => hasOpenAssignedWork(shown)) ? {
9389
10083
  content: /* @__PURE__ */ jsxRuntime.jsx(AssignedTasksHint, {
9390
10084
  work: forMe.work
9391
10085
  }),
9392
10086
  wide: !0
9393
- } : identity !== void 0 ? {
9394
- text: "Open the Workflows view",
9395
- description: "Nothing here is assigned to you"
9396
- } : {
9397
- text: "Open the Workflows view"
9398
- };
10087
+ } : unassignedLineHint({
10088
+ identityKnown: identity !== void 0,
10089
+ openTaskCount: openStageTaskCount(entry.evaluation)
10090
+ });
9399
10091
  }
9400
10092
 
9401
10093
  function StripLine({aside: aside, children: children, hint: hint, onOpen: onOpen}) {
@@ -9428,7 +10120,7 @@ function InstanceLine({entry: entry, onOpen: onOpen}) {
9428
10120
  aside: unprimed ? void 0 : /* @__PURE__ */ jsxRuntime.jsx(TaskCountSpinner, {
9429
10121
  entry: entry
9430
10122
  }),
9431
- hint: lineHint(identity, forMeWorkOf([ entry ], identity)),
10123
+ hint: lineHint(entry, identity),
9432
10124
  onOpen: onOpen,
9433
10125
  children: [
9434
10126
  /* @__PURE__ */ jsxRuntime.jsx(ui.Flex, {
@@ -9533,6 +10225,103 @@ function StartWorkflowDialogHost() {
9533
10225
  }, `${startRequest2.definition}:${startRequest2.subjectDocId ?? ""}`) : null;
9534
10226
  }
9535
10227
 
10228
+ const TOOL_TAB_LABELS = {
10229
+ overview: "Workflows",
10230
+ "all-documents": "All documents",
10231
+ "for-me": "For me"
10232
+ }, TOOL_TAB_VALUES = Object.keys(TOOL_TAB_LABELS), TOOL_TABS = TOOL_TAB_VALUES.map(value => ({
10233
+ value: value,
10234
+ label: TOOL_TAB_LABELS[value]
10235
+ })), LANDING_TAB = "overview", TASKS_TAB = "all-documents", WORKFLOW_PAGE_LABELS = {
10236
+ workflow: "Documents",
10237
+ definition: "Definition"
10238
+ }, 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 => ({
10239
+ value: value,
10240
+ label: WORKFLOW_PAGE_LABELS[value]
10241
+ })), toolRouter = router.route.create("/", [ router.route.create("/instance/:instanceId"), router.route.create(`/${LANDING_TAB}/:workflowName`, [ router.route.create("/:workflowPage") ]), router.route.create("/:workflowsTab") ]);
10242
+
10243
+ function toolTabState(tab) {
10244
+ return {
10245
+ workflowsTab: tab
10246
+ };
10247
+ }
10248
+
10249
+ function landingState() {
10250
+ return toolTabState(LANDING_TAB);
10251
+ }
10252
+
10253
+ function workflowState(workflowName) {
10254
+ return {
10255
+ workflowName: workflowName
10256
+ };
10257
+ }
10258
+
10259
+ function instanceState(instanceId) {
10260
+ return {
10261
+ instanceId: instanceId
10262
+ };
10263
+ }
10264
+
10265
+ function tabSelectionNavigates(current, tab) {
10266
+ return !(current.kind === "home" && tab === current.tab);
10267
+ }
10268
+
10269
+ function toolTabLabel(tab) {
10270
+ return TOOL_TAB_LABELS[tab];
10271
+ }
10272
+
10273
+ function backToTabLabel(tab) {
10274
+ return `Back to ${toolTabLabel(tab)}`;
10275
+ }
10276
+
10277
+ function workflowPageState(page, workflowName) {
10278
+ return page === DEFAULT_WORKFLOW_PAGE ? workflowState(workflowName) : {
10279
+ ...workflowState(workflowName),
10280
+ workflowPage: page
10281
+ };
10282
+ }
10283
+
10284
+ function toolRoute(state) {
10285
+ const {instanceId: instanceId, workflowName: workflowName} = state;
10286
+ if (typeof instanceId == "string") return {
10287
+ kind: "instance",
10288
+ instanceId: instanceId,
10289
+ tab: TASKS_TAB
10290
+ };
10291
+ if (typeof workflowName == "string") return workflowRoute(workflowName, state.workflowPage);
10292
+ const tab = pathSegmentName({
10293
+ segment: state.workflowsTab,
10294
+ names: TOOL_TAB_VALUES
10295
+ });
10296
+ return tab === void 0 ? {
10297
+ kind: "unnamed",
10298
+ tab: LANDING_TAB
10299
+ } : {
10300
+ kind: "home",
10301
+ tab: tab
10302
+ };
10303
+ }
10304
+
10305
+ function workflowRoute(workflowName, pageSegment) {
10306
+ if (pageSegment === void 0) return {
10307
+ kind: "workflow",
10308
+ workflowName: workflowName,
10309
+ tab: LANDING_TAB
10310
+ };
10311
+ const page = pathSegmentName({
10312
+ segment: pageSegment,
10313
+ names: WORKFLOW_PAGES
10314
+ });
10315
+ return page === void 0 ? {
10316
+ kind: "unnamed",
10317
+ tab: LANDING_TAB
10318
+ } : {
10319
+ kind: page,
10320
+ workflowName: workflowName,
10321
+ tab: LANDING_TAB
10322
+ };
10323
+ }
10324
+
9536
10325
  const WorkflowsToolRoot = react.lazy(() => Promise.resolve().then(function() {
9537
10326
  return require("./workflows-tool-root.cjs");
9538
10327
  }));
@@ -9550,7 +10339,7 @@ const workflowsTool = {
9550
10339
  title: "Workflows",
9551
10340
  icon: Transfer.TransferIcon,
9552
10341
  component: LazyWorkflowsTool,
9553
- router: router.route.create("/", [ router.route.create("/instance/:instanceId"), router.route.create("/definition/:definitionName"), router.route.create("/:workflowsTab") ]),
10342
+ router: toolRouter,
9554
10343
  canHandleIntent: (intent, params) => handlesWorkflowIntent(intent, params),
9555
10344
  getIntentState: (intent, params) => workflowIntentState(intent, params)
9556
10345
  }, LOCKABLE_ACTIONS = /* @__PURE__ */ new Map([ [ "delete", "delete" ], [ "publish", "publish" ], [ "unpublish", "unpublish" ] ]);
@@ -9654,14 +10443,20 @@ exports.CautionNote = CautionNote;
9654
10443
 
9655
10444
  exports.CollapsibleBand = CollapsibleBand;
9656
10445
 
10446
+ exports.CountedLabel = CountedLabel;
10447
+
9657
10448
  exports.DismissablePopover = DismissablePopover;
9658
10449
 
9659
10450
  exports.DocPreviewLink = DocPreviewLink;
9660
10451
 
9661
10452
  exports.DocRefFace = DocRefFace;
9662
10453
 
10454
+ exports.EmptyState = EmptyState;
10455
+
9663
10456
  exports.ForMeEmptyState = ForMeEmptyState;
9664
10457
 
10458
+ exports.Hairline = Hairline;
10459
+
9665
10460
  exports.HintedMenuButton = HintedMenuButton;
9666
10461
 
9667
10462
  exports.HoverHint = HoverHint;
@@ -9674,6 +10469,8 @@ exports.LinkChip = LinkChip;
9674
10469
 
9675
10470
  exports.LoadingRow = LoadingRow;
9676
10471
 
10472
+ exports.LogEventOnMount = LogEventOnMount;
10473
+
9677
10474
  exports.MetaRow = MetaRow;
9678
10475
 
9679
10476
  exports.SpinnerSlot = SpinnerSlot;
@@ -9682,6 +10479,10 @@ exports.StageFace = StageFace;
9682
10479
 
9683
10480
  exports.StaleLock = StaleLock;
9684
10481
 
10482
+ exports.TOAST_ID = TOAST_ID;
10483
+
10484
+ exports.TOOL_TABS = TOOL_TABS;
10485
+
9685
10486
  exports.TabSwitch = TabSwitch;
9686
10487
 
9687
10488
  exports.TrailingHairline = TrailingHairline;
@@ -9692,26 +10493,44 @@ exports.UserAvatar = UserAvatar;
9692
10493
 
9693
10494
  exports.WORKFLOW_API_VERSION = WORKFLOW_API_VERSION;
9694
10495
 
10496
+ exports.WORKFLOW_PAGE_TABS = WORKFLOW_PAGE_TABS;
10497
+
10498
+ exports.WorkflowAbortDialogOpened = WorkflowAbortDialogOpened;
10499
+
10500
+ exports.WorkflowAbortDialogSubmitted = WorkflowAbortDialogSubmitted;
10501
+
10502
+ exports.WorkflowBoardWorkflowSelected = WorkflowBoardWorkflowSelected;
10503
+
10504
+ exports.WorkflowDefinitionDetailViewed = WorkflowDefinitionDetailViewed;
10505
+
9695
10506
  exports.WorkflowInstanceDetailViewed = WorkflowInstanceDetailViewed;
9696
10507
 
9697
10508
  exports.WorkflowTaskFiltersApplied = WorkflowTaskFiltersApplied;
9698
10509
 
10510
+ exports.WorkflowTitleSeedDrifted = WorkflowTitleSeedDrifted;
10511
+
9699
10512
  exports.WorkflowToolOpened = WorkflowToolOpened;
9700
10513
 
9701
10514
  exports.activityRowProps = activityRowProps;
9702
10515
 
9703
10516
  exports.assigneeUserIdsOf = assigneeUserIdsOf;
9704
10517
 
10518
+ exports.backToTabLabel = backToTabLabel;
10519
+
9705
10520
  exports.committedRowFace = committedRowFace;
9706
10521
 
9707
- exports.dateControlKind = dateControlKind;
10522
+ exports.createSubscribers = createSubscribers;
10523
+
10524
+ exports.datesOf = datesOf;
9708
10525
 
9709
- exports.dateControlValue = dateControlValue;
10526
+ exports.definitionFingerprint = definitionFingerprint;
9710
10527
 
9711
10528
  exports.definitionSnapshotOf = definitionSnapshotOf;
9712
10529
 
9713
10530
  exports.describeError = describeError;
9714
10531
 
10532
+ exports.dueDatesOf = dueDatesOf;
10533
+
9715
10534
  exports.findActivity = findActivity;
9716
10535
 
9717
10536
  exports.findActivityNode = findActivityNode;
@@ -9722,43 +10541,53 @@ exports.formatDateTime = formatDateTime;
9722
10541
 
9723
10542
  exports.formatShortAgo = formatShortAgo;
9724
10543
 
10544
+ exports.formatShortDateTime = formatShortDateTime;
10545
+
10546
+ exports.formatTimeAgo = formatTimeAgo;
10547
+
9725
10548
  exports.gdrLocality = gdrLocality;
9726
10549
 
9727
10550
  exports.instanceBreadcrumb = instanceBreadcrumb;
9728
10551
 
10552
+ exports.instanceState = instanceState;
10553
+
9729
10554
  exports.instanceTitle = instanceTitle;
9730
10555
 
9731
10556
  exports.isActivityAssignedTo = isActivityAssignedTo;
9732
10557
 
9733
10558
  exports.isEvaluationStale = isEvaluationStale;
9734
10559
 
10560
+ exports.isLiveEntry = isLiveEntry;
10561
+
9735
10562
  exports.isOpenActivityStatus = isOpenActivityStatus;
9736
10563
 
10564
+ exports.landingState = landingState;
10565
+
10566
+ exports.mappingIssueDetail = mappingIssueDetail;
10567
+
10568
+ exports.namesNoDeployedDefinition = namesNoDeployedDefinition;
10569
+
9737
10570
  exports.openActivityGone = openActivityGone;
9738
10571
 
9739
10572
  exports.openableSchemaType = openableSchemaType;
9740
10573
 
9741
- exports.parseDateFieldValue = parseDateFieldValue;
9742
-
9743
10574
  exports.parseStoredDateValue = parseStoredDateValue;
9744
10575
 
9745
- exports.pathSegmentTab = pathSegmentTab;
9746
-
9747
10576
  exports.readDeployedDefinitions = readDeployedDefinitions;
9748
10577
 
9749
10578
  exports.rowAssignControlState = rowAssignControlState;
9750
10579
 
9751
10580
  exports.rowDateControlState = rowDateControlState;
9752
10581
 
9753
- exports.serializeDateFieldValue = serializeDateFieldValue;
9754
-
9755
10582
  exports.stageTitle = stageTitle;
9756
10583
 
9757
10584
  exports.stateByActivity = stateByActivity;
9758
10585
 
9759
- exports.stopRowClick = stopRowClick;
10586
+ exports.tabSelectionNavigates = tabSelectionNavigates;
10587
+
10588
+ exports.toolRoute = toolRoute;
9760
10589
 
9761
- exports.stopRowMouseDown = stopRowMouseDown;
10590
+ exports.toolTabState = toolTabState;
9762
10591
 
9763
10592
  exports.useAssignmentIdentity = useAssignmentIdentity;
9764
10593
 
@@ -9766,18 +10595,16 @@ exports.useAvatarSize = useAvatarSize;
9766
10595
 
9767
10596
  exports.useBadgeCapTrim = useBadgeCapTrim;
9768
10597
 
9769
- exports.useClosableToast = useClosableToast;
9770
-
9771
10598
  exports.useContainerToken = useContainerToken;
9772
10599
 
9773
10600
  exports.useDefinition = useDefinition;
9774
10601
 
10602
+ exports.useDelayedFlag = useDelayedFlag;
10603
+
9775
10604
  exports.useLogEventOnMount = useLogEventOnMount;
9776
10605
 
9777
10606
  exports.useProjectMembers = useProjectMembers;
9778
10607
 
9779
- exports.useSaveField = useSaveField;
9780
-
9781
10608
  exports.useSpaceToken = useSpaceToken;
9782
10609
 
9783
10610
  exports.useUserDisplay = useUserDisplay;
@@ -9788,8 +10615,14 @@ exports.useWorkflowContext = useWorkflowContext;
9788
10615
 
9789
10616
  exports.useWorkflowInstanceEntry = useWorkflowInstanceEntry;
9790
10617
 
10618
+ exports.useWorkflowToast = useWorkflowToast;
10619
+
9791
10620
  exports.workflowDefaultDocumentNode = workflowDefaultDocumentNode;
9792
10621
 
10622
+ exports.workflowPageState = workflowPageState;
10623
+
10624
+ exports.workflowState = workflowState;
10625
+
9793
10626
  exports.workflowStudioPlugin = workflowStudioPlugin;
9794
10627
 
9795
10628
  exports.workflowsView = workflowsView;