@sanity/workflow-studio-plugin 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +241 -0
- package/dist/_chunks-cjs/index.cjs +1273 -569
- package/dist/_chunks-cjs/workflows-tool-root.cjs +210 -599
- package/dist/_chunks-es/index.js +1296 -574
- package/dist/_chunks-es/workflows-tool-root.js +210 -603
- package/package.json +12 -12
|
@@ -1,22 +1,16 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
|
|
3
|
-
import { Text,
|
|
3
|
+
import { Text, Stack, Flex, Button, Box, Card, Grid, TextInput, Dialog, Badge, useTheme_v2, MenuButton, Menu, MenuItem, TabPanel } from "@sanity/ui";
|
|
4
4
|
|
|
5
5
|
import { useWorkflowTelemetry } from "@sanity/workflow-react";
|
|
6
6
|
|
|
7
|
-
import { useState,
|
|
7
|
+
import { useState, useEffect, useMemo, useRef, useSyncExternalStore, useCallback } from "react";
|
|
8
8
|
|
|
9
9
|
import { useRouter } from "sanity/router";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { isLiveEntry, AbortWorkflowDialog, isEvaluationStale, useSpaceToken, DocPreviewLink, SpinnerSlot, useWorkflowInstanceEntry, useLogEventOnMount, WorkflowInstanceDetailViewed, LoadingRow, InvalidDocNotice, useDefinition, useContainerToken, openActivityGone, InstanceSnapshotBody, ActivityLog, ActivityDetailDialog, instanceBreadcrumb, CodeChip, instanceTitle, namesNoDeployedDefinition, mappingIssueDetail, formatTimeAgo, formatShortDateTime, useWorkflowContext, readDeployedDefinitions, describeError, NoteBanner, EmptyState, HoverHint, DismissablePopover, createSubscribers, useDelayedFlag, definitionSnapshotOf, findActivity, stageTitle, stateByActivity, findActivityNode, rowDateControlState, rowAssignControlState, isRoleAssignedTo, isActivityAssignedTo, assigneesOf, dueDatesOf, datesOf, isOpenActivityStatus, committedRowFace, activityRowProps, gdrLocality, TOAST_ID, WORKFLOW_API_VERSION, useWorkflowToast, openableSchemaType, formatDate, parseStoredDateValue, useUserDisplays, useProjectMembers, WorkflowTaskFiltersApplied, DocRefFace, Hairline, LinkChip, BreadcrumbTail, StageFace, StaleLock, ActivityRow, WorkflowTitleSeedDrifted, UnreadableDocsNote, RoleAssignedNotice, ForMeEmptyState, useBadgeCapTrim, CountedLabel, MetaRow, TabSwitch, WORKFLOW_PAGE_TABS, definitionFingerprint, LogEventOnMount, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, toolRoute, landingState, TOOL_TABS, tabSelectionNavigates, WorkflowToolOpened, toolTabState, workflowPageState, instanceState, backToTabLabel, useAssignmentIdentity, workflowState } from "./index.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
import { CogIcon } from "@sanity/icons/Cog";
|
|
16
|
-
|
|
17
|
-
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
18
|
-
|
|
19
|
-
import { entryDocRefs, startKindOf, isSubjectEntry, isInputSourced, latestDeployedDefinitions, terminalState, isStartableDefinition, toBareId, findOpenStageEntry } from "@sanity/workflow-engine";
|
|
13
|
+
import { entryDocRefs, startKindOf, isSubjectEntry, isInputSourced, errorMessage, latestDeployedDefinitions, terminalState, isStartableDefinition, toBareId, findOpenStageEntry } from "@sanity/workflow-engine";
|
|
20
14
|
|
|
21
15
|
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
22
16
|
|
|
@@ -46,24 +40,16 @@ import { endOfDay } from "date-fns/endOfDay";
|
|
|
46
40
|
|
|
47
41
|
import { createPortal } from "react-dom";
|
|
48
42
|
|
|
43
|
+
import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
|
|
44
|
+
|
|
45
|
+
import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
|
|
46
|
+
|
|
49
47
|
import { isReferenceSchemaType } from "@sanity/types";
|
|
50
48
|
|
|
51
49
|
import { WorkflowDiagram } from "@sanity/workflow-diagram";
|
|
52
50
|
|
|
53
|
-
import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
|
|
54
|
-
|
|
55
51
|
import { useReducedMotion, motion, useSpring, useMotionValueEvent } from "motion/react";
|
|
56
52
|
|
|
57
|
-
function CodeChip({value: value}) {
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
-
return jsx("code", {
|
|
60
|
-
style: {
|
|
61
|
-
overflowWrap: "anywhere"
|
|
62
|
-
},
|
|
63
|
-
children: value
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
|
|
67
53
|
function SectionHeading({children: children}) {
|
|
68
54
|
/* @__PURE__ */
|
|
69
55
|
return jsx(Text, {
|
|
@@ -73,146 +59,6 @@ function SectionHeading({children: children}) {
|
|
|
73
59
|
});
|
|
74
60
|
}
|
|
75
61
|
|
|
76
|
-
function normalizeAbortReason(raw) {
|
|
77
|
-
const trimmed = raw.trim();
|
|
78
|
-
return trimmed === "" ? void 0 : trimmed;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function abortCommitReport(args) {
|
|
82
|
-
return args.changed ? {
|
|
83
|
-
outcome: "aborted",
|
|
84
|
-
toast: abortedToast(args.title)
|
|
85
|
-
} : {
|
|
86
|
-
outcome: "already-settled",
|
|
87
|
-
toast: abortAlreadySettledToast(args.title)
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function abortedToast(title) {
|
|
92
|
-
return {
|
|
93
|
-
id: TOAST_ID.abort,
|
|
94
|
-
status: "info",
|
|
95
|
-
title: `Aborted “${title}”`
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
function abortAlreadySettledToast(title) {
|
|
100
|
-
return {
|
|
101
|
-
id: TOAST_ID.abort,
|
|
102
|
-
status: "info",
|
|
103
|
-
title: `“${title}” had already finished`,
|
|
104
|
-
description: "Nothing was aborted — the workflow reached a terminal state first."
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function abortUnconfirmedToast(args) {
|
|
109
|
-
return {
|
|
110
|
-
id: TOAST_ID.abort,
|
|
111
|
-
status: "warning",
|
|
112
|
-
title: `Couldn’t confirm “${args.title}” was aborted`,
|
|
113
|
-
description: `${describeError(args.err)} — the abort may still have committed, so check the workflow’s state before trying again.`
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
|
|
118
|
-
const {engine: engine} = useWorkflowContext(), definition = useDefinition(entry), toast = useWorkflowToast(), telemetry = useWorkflowTelemetry(), [typedReason, setTypedReason] = useState(""), [pending, setPending] = useState(!1), {instance: instance} = entry, title = instanceTitle(instance, definition), reason = normalizeAbortReason(typedReason), reasonInputId = `abort-workflow-reason-${instance._id}`;
|
|
119
|
-
useLogEventOnMount(WorkflowAbortDialogOpened, {
|
|
120
|
-
instanceId: instance._id
|
|
121
|
-
});
|
|
122
|
-
const confirm = async () => {
|
|
123
|
-
if (reason === void 0) return;
|
|
124
|
-
const log = outcome => telemetry.log(WorkflowAbortDialogSubmitted, {
|
|
125
|
-
instanceId: instance._id,
|
|
126
|
-
outcome: outcome
|
|
127
|
-
});
|
|
128
|
-
setPending(!0);
|
|
129
|
-
try {
|
|
130
|
-
const {changed: changed} = await engine.abortInstance({
|
|
131
|
-
instanceId: instance._id,
|
|
132
|
-
reason: reason
|
|
133
|
-
}), report = abortCommitReport({
|
|
134
|
-
title: title,
|
|
135
|
-
changed: changed
|
|
136
|
-
});
|
|
137
|
-
toast.push(report.toast), log(report.outcome), onClose();
|
|
138
|
-
} catch (err) {
|
|
139
|
-
toast.push(abortUnconfirmedToast({
|
|
140
|
-
title: title,
|
|
141
|
-
err: err
|
|
142
|
-
})), log("unconfirmed"), setPending(!1);
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
/* @__PURE__ */
|
|
146
|
-
return jsx(Dialog, {
|
|
147
|
-
footer: /* @__PURE__ */ jsx(Box, {
|
|
148
|
-
padding: 3,
|
|
149
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
150
|
-
gap: 2,
|
|
151
|
-
justify: "flex-end",
|
|
152
|
-
children: [
|
|
153
|
-
/* @__PURE__ */ jsx(Button, {
|
|
154
|
-
disabled: pending,
|
|
155
|
-
fontSize: 1,
|
|
156
|
-
mode: "bleed",
|
|
157
|
-
onClick: onClose,
|
|
158
|
-
padding: 2,
|
|
159
|
-
text: "Cancel"
|
|
160
|
-
}),
|
|
161
|
-
/* @__PURE__ */ jsx(Button, {
|
|
162
|
-
disabled: reason === void 0,
|
|
163
|
-
fontSize: 1,
|
|
164
|
-
loading: pending,
|
|
165
|
-
onClick: () => {
|
|
166
|
-
confirm();
|
|
167
|
-
},
|
|
168
|
-
padding: 2,
|
|
169
|
-
text: "Abort workflow",
|
|
170
|
-
tone: "critical"
|
|
171
|
-
}) ]
|
|
172
|
-
})
|
|
173
|
-
}),
|
|
174
|
-
header: "Abort workflow",
|
|
175
|
-
id: `abort-workflow-${instance._id}`,
|
|
176
|
-
onClose: () => {
|
|
177
|
-
pending || onClose();
|
|
178
|
-
},
|
|
179
|
-
width: 0,
|
|
180
|
-
children: /* @__PURE__ */ jsx(Box, {
|
|
181
|
-
padding: 4,
|
|
182
|
-
children: /* @__PURE__ */ jsxs(Stack, {
|
|
183
|
-
gap: 5,
|
|
184
|
-
children: [
|
|
185
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
186
|
-
size: 1,
|
|
187
|
-
children: [ "Abort the ",
|
|
188
|
-
/* @__PURE__ */ jsx("strong", {
|
|
189
|
-
children: title
|
|
190
|
-
}), " workflow? This cannot be undone." ]
|
|
191
|
-
}),
|
|
192
|
-
/* @__PURE__ */ jsxs(Stack, {
|
|
193
|
-
gap: 3,
|
|
194
|
-
children: [
|
|
195
|
-
/* @__PURE__ */ jsx(Text, {
|
|
196
|
-
as: "label",
|
|
197
|
-
htmlFor: reasonInputId,
|
|
198
|
-
size: 1,
|
|
199
|
-
weight: "medium",
|
|
200
|
-
children: "Reason"
|
|
201
|
-
}),
|
|
202
|
-
/* @__PURE__ */ jsx(TextInput, {
|
|
203
|
-
autoFocus: !0,
|
|
204
|
-
fontSize: 1,
|
|
205
|
-
id: reasonInputId,
|
|
206
|
-
onChange: event => setTypedReason(event.currentTarget.value),
|
|
207
|
-
placeholder: "Why is this workflow being stopped?",
|
|
208
|
-
value: typedReason
|
|
209
|
-
}) ]
|
|
210
|
-
}) ]
|
|
211
|
-
})
|
|
212
|
-
})
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
62
|
function AbortWorkflowSection({entry: entry}) {
|
|
217
63
|
const [aborting, setAborting] = useState(!1);
|
|
218
64
|
return isLiveEntry(entry) ? /* @__PURE__ */ jsxs(Stack, {
|
|
@@ -237,361 +83,6 @@ function AbortWorkflowSection({entry: entry}) {
|
|
|
237
83
|
}) : null;
|
|
238
84
|
}
|
|
239
85
|
|
|
240
|
-
function makeTitleResolver(definition) {
|
|
241
|
-
const activities = (definition?.stages ?? []).flatMap(s => s.activities ?? []);
|
|
242
|
-
return {
|
|
243
|
-
action: (activityName, actionName) => activities.find(t => t.name === activityName)?.actions?.find(a => a.name === actionName)?.title ?? actionName,
|
|
244
|
-
activity: name => activities.find(t => t.name === name)?.title ?? name,
|
|
245
|
-
stage: name => stageTitle(definition, name)
|
|
246
|
-
};
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
function attributed(actor, ...parts) {
|
|
250
|
-
if (actor) return {
|
|
251
|
-
actorId: actor.id,
|
|
252
|
-
parts: parts
|
|
253
|
-
};
|
|
254
|
-
const [first, ...rest] = parts;
|
|
255
|
-
return typeof first != "string" ? {
|
|
256
|
-
actorId: void 0,
|
|
257
|
-
parts: parts
|
|
258
|
-
} : {
|
|
259
|
-
actorId: void 0,
|
|
260
|
-
parts: [ first.charAt(0).toUpperCase() + first.slice(1), ...rest ]
|
|
261
|
-
};
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
function standalone(...parts) {
|
|
265
|
-
return {
|
|
266
|
-
actorId: void 0,
|
|
267
|
-
parts: parts
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
const historyLiner = {
|
|
272
|
-
stageEntered: (h, titles) => attributed(h.actor, `entered ${titles.stage(h.stage)}${h.fromStage ? ` from ${titles.stage(h.fromStage)}` : ""}`),
|
|
273
|
-
stageExited: (h, titles) => attributed(h.actor, `left ${titles.stage(h.stage)} for ${titles.stage(h.toStage)}`),
|
|
274
|
-
activityStatusChanged: (h, titles) => attributed(h.actor, `changed ${titles.activity(h.activity)}: ${h.from} → ${h.to}`),
|
|
275
|
-
actionFired: (h, titles) => {
|
|
276
|
-
const phrase = `fired ${titles.action(h.activity, h.action)} on ${titles.activity(h.activity)}`;
|
|
277
|
-
return h.triggered ? standalone(`The workflow ${phrase}`) : attributed(h.actor, phrase);
|
|
278
|
-
},
|
|
279
|
-
transitionFired: (h, titles) => attributed(h.actor, `moved ${titles.stage(h.fromStage)} → ${titles.stage(h.toStage)}`),
|
|
280
|
-
effectQueued: h => standalone("Effect ", {
|
|
281
|
-
code: h.effect
|
|
282
|
-
}, " queued"),
|
|
283
|
-
effectCompleted: h => {
|
|
284
|
-
const detail = h.detail ? ` — ${h.detail}` : "";
|
|
285
|
-
return h.actor ? attributed(h.actor, "resolved effect ", {
|
|
286
|
-
code: h.effect
|
|
287
|
-
}, ` — ${h.status}${detail}`) : standalone("Effect ", {
|
|
288
|
-
code: h.effect
|
|
289
|
-
}, ` ${h.status}${detail}`);
|
|
290
|
-
},
|
|
291
|
-
effectClaimReleased: h => attributed(h.actor, "released a stale claim on effect ", {
|
|
292
|
-
code: h.effect
|
|
293
|
-
}, ` ${h.via === "sweep" ? "via the sweeper" : "via a drain takeover"}`),
|
|
294
|
-
spawned: (h, titles) => attributed(h.actor, `spawned a child workflow from ${titles.activity(h.activity)}`),
|
|
295
|
-
subworkflowAdopted: (h, titles) => standalone(`Re-adopted child workflow into ${titles.activity(h.activity)} on re-entering ${titles.stage(h.stage)}`),
|
|
296
|
-
subworkflowResolved: (h, titles) => standalone(`Child workflow of ${titles.activity(h.activity)} ${h.status === "done" ? "completed" : "was aborted"}`),
|
|
297
|
-
subworkflowOrphaned: h => standalone(`Orphaned child workflow reached terminal — ${h.detail}`),
|
|
298
|
-
aborted: (h, titles) => attributed(h.actor, `aborted the workflow at ${titles.stage(h.stage)}${h.reason ? ` — ${h.reason}` : ""}`),
|
|
299
|
-
opApplied: (h, titles) => h.action ? attributed(h.actor, `changed state via action ${titles.action(h.activity ?? "", h.action)}`) : h.activity ? attributed(h.actor, `changed state via activity ${titles.activity(h.activity)}`) : attributed(h.actor, "changed state via ", {
|
|
300
|
-
code: h.opType
|
|
301
|
-
}),
|
|
302
|
-
fieldQueryDiscarded: h => standalone({
|
|
303
|
-
code: JSON.stringify(h)
|
|
304
|
-
})
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
function historyLine(h, titles) {
|
|
308
|
-
return historyLiner[h._type](h, titles);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
function historyWindow(args) {
|
|
312
|
-
const {entries: entries, expanded: expanded} = args;
|
|
313
|
-
return {
|
|
314
|
-
overflows: entries.length > 6,
|
|
315
|
-
shown: expanded ? entries : entries.slice(0, 6)
|
|
316
|
-
};
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function PendingEffectRows({instance: instance, now: now}) {
|
|
320
|
-
const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1), pending = instance.pendingEffects ?? [];
|
|
321
|
-
if (pending.length === 0) return null;
|
|
322
|
-
const runRegistered = async () => {
|
|
323
|
-
setBusy(!0);
|
|
324
|
-
try {
|
|
325
|
-
await drainEffectsFor(instance._id), toast.push({
|
|
326
|
-
id: TOAST_ID.effectsDrain,
|
|
327
|
-
status: "info",
|
|
328
|
-
title: "Ran the registered handlers"
|
|
329
|
-
});
|
|
330
|
-
} catch (err) {
|
|
331
|
-
toast.push({
|
|
332
|
-
id: TOAST_ID.effectsDrain,
|
|
333
|
-
status: "error",
|
|
334
|
-
title: "Failed to run pending effects",
|
|
335
|
-
description: describeError(err)
|
|
336
|
-
});
|
|
337
|
-
} finally {
|
|
338
|
-
setBusy(!1);
|
|
339
|
-
}
|
|
340
|
-
}, resolve = async (effectKey, status) => {
|
|
341
|
-
setBusy(!0);
|
|
342
|
-
try {
|
|
343
|
-
await completeEffectFor(instance._id, {
|
|
344
|
-
effectKey: effectKey,
|
|
345
|
-
status: status,
|
|
346
|
-
detail: "Resolved in Studio"
|
|
347
|
-
}), toast.push({
|
|
348
|
-
id: TOAST_ID.effectResolve,
|
|
349
|
-
status: "info",
|
|
350
|
-
title: `Effect marked as ${status}`
|
|
351
|
-
});
|
|
352
|
-
} catch (err) {
|
|
353
|
-
toast.push({
|
|
354
|
-
id: TOAST_ID.effectResolve,
|
|
355
|
-
status: "error",
|
|
356
|
-
title: "Failed to resolve the effect",
|
|
357
|
-
description: describeError(err)
|
|
358
|
-
});
|
|
359
|
-
} finally {
|
|
360
|
-
setBusy(!1);
|
|
361
|
-
}
|
|
362
|
-
};
|
|
363
|
-
/* @__PURE__ */
|
|
364
|
-
return jsx(Fragment, {
|
|
365
|
-
children: pending.map(fx => /* @__PURE__ */ jsx(PendingEffectRow, {
|
|
366
|
-
busy: busy,
|
|
367
|
-
effect: fx,
|
|
368
|
-
instanceId: instance._id,
|
|
369
|
-
now: now,
|
|
370
|
-
onResolve: status => resolve(fx._key, status),
|
|
371
|
-
onRunHandlers: runRegistered,
|
|
372
|
-
registered: fx.name in effectHandlers
|
|
373
|
-
}, fx._key))
|
|
374
|
-
});
|
|
375
|
-
}
|
|
376
|
-
|
|
377
|
-
function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, now: now, onResolve: onResolve, onRunHandlers: onRunHandlers, registered: registered}) {
|
|
378
|
-
const label = effect.title ?? effect.name, avatarSize = useAvatarSize();
|
|
379
|
-
/* @__PURE__ */
|
|
380
|
-
return jsxs(Flex, {
|
|
381
|
-
align: "center",
|
|
382
|
-
gap: 2,
|
|
383
|
-
children: [
|
|
384
|
-
/* @__PURE__ */ jsx(FeedGlyph, {
|
|
385
|
-
hint: "Waiting to run",
|
|
386
|
-
icon: /* @__PURE__ */ jsx(ClockIcon, {})
|
|
387
|
-
}),
|
|
388
|
-
/* @__PURE__ */ jsxs(Flex, {
|
|
389
|
-
align: "center",
|
|
390
|
-
flex: 1,
|
|
391
|
-
gap: 1,
|
|
392
|
-
style: {
|
|
393
|
-
minWidth: 0
|
|
394
|
-
},
|
|
395
|
-
children: [
|
|
396
|
-
/* @__PURE__ */ jsx(Box, {
|
|
397
|
-
style: {
|
|
398
|
-
minWidth: 0
|
|
399
|
-
},
|
|
400
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
401
|
-
size: 1,
|
|
402
|
-
textOverflow: "ellipsis",
|
|
403
|
-
children: [ effect.title ?? /* @__PURE__ */ jsx(CodeChip, {
|
|
404
|
-
value: effect.name
|
|
405
|
-
}), registered ? " — ready to run in Studio" : " — waiting for the runtime" ]
|
|
406
|
-
})
|
|
407
|
-
}),
|
|
408
|
-
/* @__PURE__ */ jsx(FeedStamp, {
|
|
409
|
-
at: effect.queuedAt,
|
|
410
|
-
now: now
|
|
411
|
-
}) ]
|
|
412
|
-
}),
|
|
413
|
-
/* @__PURE__ */ jsx(Flex, {
|
|
414
|
-
align: "center",
|
|
415
|
-
flex: "none",
|
|
416
|
-
style: {
|
|
417
|
-
height: avatarSize
|
|
418
|
-
},
|
|
419
|
-
children: /* @__PURE__ */ jsx(HintedMenuButton, {
|
|
420
|
-
button: /* @__PURE__ */ jsx(Button, {
|
|
421
|
-
"aria-label": `Resolve ${label}`,
|
|
422
|
-
disabled: busy,
|
|
423
|
-
fontSize: 1,
|
|
424
|
-
icon: EllipsisHorizontalIcon,
|
|
425
|
-
mode: "bleed",
|
|
426
|
-
padding: 2
|
|
427
|
-
}),
|
|
428
|
-
hint: "Resolve",
|
|
429
|
-
id: `pending-effect-menu-${instanceId}-${effect._key}`,
|
|
430
|
-
menu: /* @__PURE__ */ jsxs(Menu, {
|
|
431
|
-
children: [ registered ? /* @__PURE__ */ jsx(MenuItem, {
|
|
432
|
-
onClick: onRunHandlers,
|
|
433
|
-
text: "Run registered handlers"
|
|
434
|
-
}) : null,
|
|
435
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
436
|
-
onClick: () => onResolve("done"),
|
|
437
|
-
text: "Mark done"
|
|
438
|
-
}),
|
|
439
|
-
/* @__PURE__ */ jsx(MenuItem, {
|
|
440
|
-
onClick: () => onResolve("failed"),
|
|
441
|
-
text: "Mark failed",
|
|
442
|
-
tone: "critical"
|
|
443
|
-
}) ]
|
|
444
|
-
}),
|
|
445
|
-
popover: {
|
|
446
|
-
placement: "bottom-end",
|
|
447
|
-
portal: !0
|
|
448
|
-
}
|
|
449
|
-
})
|
|
450
|
-
}) ]
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function ActivityLog({instance: instance, definition: definition}) {
|
|
455
|
-
const titles = useMemo(() => makeTitleResolver(definition), [ definition ]), entries = useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = useState(() => /* @__PURE__ */ new Date), [expanded, setExpanded] = useState(!1);
|
|
456
|
-
useEffect(() => {
|
|
457
|
-
const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
|
|
458
|
-
return () => clearInterval(timer);
|
|
459
|
-
}, []);
|
|
460
|
-
const history = historyWindow({
|
|
461
|
-
entries: entries,
|
|
462
|
-
expanded: expanded
|
|
463
|
-
});
|
|
464
|
-
return entries.length === 0 && (instance.pendingEffects ?? []).length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
465
|
-
muted: !0,
|
|
466
|
-
size: 1,
|
|
467
|
-
children: "No activity yet"
|
|
468
|
-
}) : /* @__PURE__ */ jsxs(Stack, {
|
|
469
|
-
gap: 3,
|
|
470
|
-
children: [
|
|
471
|
-
/* @__PURE__ */ jsx(PendingEffectRows, {
|
|
472
|
-
instance: instance,
|
|
473
|
-
now: now
|
|
474
|
-
}), history.shown.map(h => /* @__PURE__ */ jsx(HistoryRow, {
|
|
475
|
-
entry: h,
|
|
476
|
-
now: now,
|
|
477
|
-
titles: titles
|
|
478
|
-
}, h._key)), history.overflows ? /* @__PURE__ */ jsx(Flex, {
|
|
479
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
480
|
-
fontSize: 1,
|
|
481
|
-
mode: "bleed",
|
|
482
|
-
onClick: () => setExpanded(!expanded),
|
|
483
|
-
padding: 2,
|
|
484
|
-
text: expanded ? "Show less history" : "Show all history"
|
|
485
|
-
})
|
|
486
|
-
}) : null ]
|
|
487
|
-
});
|
|
488
|
-
}
|
|
489
|
-
|
|
490
|
-
function FeedStamp({at: at, now: now}) {
|
|
491
|
-
/* @__PURE__ */
|
|
492
|
-
return jsxs(Flex, {
|
|
493
|
-
align: "center",
|
|
494
|
-
flex: "none",
|
|
495
|
-
gap: 1,
|
|
496
|
-
children: [
|
|
497
|
-
/* @__PURE__ */ jsx(Text, {
|
|
498
|
-
muted: !0,
|
|
499
|
-
size: 1,
|
|
500
|
-
children: "·"
|
|
501
|
-
}),
|
|
502
|
-
/* @__PURE__ */ jsx(HoverHint, {
|
|
503
|
-
text: formatDateTime(at),
|
|
504
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
505
|
-
muted: !0,
|
|
506
|
-
size: 1,
|
|
507
|
-
style: {
|
|
508
|
-
whiteSpace: "nowrap"
|
|
509
|
-
},
|
|
510
|
-
children: formatShortAgo(at, now)
|
|
511
|
-
})
|
|
512
|
-
}) ]
|
|
513
|
-
});
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
function HistoryRow({entry: entry, now: now, titles: titles}) {
|
|
517
|
-
const line = historyLine(entry, titles);
|
|
518
|
-
/* @__PURE__ */
|
|
519
|
-
return jsxs(Flex, {
|
|
520
|
-
align: "center",
|
|
521
|
-
gap: 2,
|
|
522
|
-
children: [ line.actorId === void 0 ? /* @__PURE__ */ jsx(FeedGlyph, {
|
|
523
|
-
hint: "The workflow",
|
|
524
|
-
icon: /* @__PURE__ */ jsx(CogIcon, {})
|
|
525
|
-
}) : /* @__PURE__ */ jsx(UserAvatar, {
|
|
526
|
-
id: line.actorId
|
|
527
|
-
}),
|
|
528
|
-
/* @__PURE__ */ jsxs(Flex, {
|
|
529
|
-
align: "center",
|
|
530
|
-
flex: 1,
|
|
531
|
-
gap: 1,
|
|
532
|
-
style: {
|
|
533
|
-
minWidth: 0
|
|
534
|
-
},
|
|
535
|
-
children: [
|
|
536
|
-
/* @__PURE__ */ jsx(Box, {
|
|
537
|
-
style: {
|
|
538
|
-
minWidth: 0
|
|
539
|
-
},
|
|
540
|
-
children: /* @__PURE__ */ jsxs(Text, {
|
|
541
|
-
muted: !0,
|
|
542
|
-
size: 1,
|
|
543
|
-
textOverflow: "ellipsis",
|
|
544
|
-
children: [ line.actorId === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
|
|
545
|
-
children: [
|
|
546
|
-
/* @__PURE__ */ jsx(ActorNameSpan, {
|
|
547
|
-
id: line.actorId
|
|
548
|
-
}), " " ]
|
|
549
|
-
}), line.parts.map((part, index) => typeof part == "string" ? part : /* @__PURE__ */ jsx(CodeChip, {
|
|
550
|
-
value: part.code
|
|
551
|
-
}, index)) ]
|
|
552
|
-
})
|
|
553
|
-
}),
|
|
554
|
-
/* @__PURE__ */ jsx(FeedStamp, {
|
|
555
|
-
at: entry.at,
|
|
556
|
-
now: now
|
|
557
|
-
}) ]
|
|
558
|
-
}) ]
|
|
559
|
-
});
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
function ActorNameSpan({id: id}) {
|
|
563
|
-
const {name: name} = useUserDisplay(id), {font: font} = useTheme_v2();
|
|
564
|
-
/* @__PURE__ */
|
|
565
|
-
return jsx("span", {
|
|
566
|
-
style: {
|
|
567
|
-
fontWeight: font.text.weights.medium
|
|
568
|
-
},
|
|
569
|
-
children: name
|
|
570
|
-
});
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function FeedGlyph({hint: hint, icon: icon}) {
|
|
574
|
-
const size = useAvatarSize();
|
|
575
|
-
/* @__PURE__ */
|
|
576
|
-
return jsx(HoverHint, {
|
|
577
|
-
text: hint,
|
|
578
|
-
children: /* @__PURE__ */ jsx(Flex, {
|
|
579
|
-
align: "center",
|
|
580
|
-
flex: "none",
|
|
581
|
-
justify: "center",
|
|
582
|
-
style: {
|
|
583
|
-
height: size,
|
|
584
|
-
width: size
|
|
585
|
-
},
|
|
586
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
587
|
-
muted: !0,
|
|
588
|
-
size: 1,
|
|
589
|
-
children: icon
|
|
590
|
-
})
|
|
591
|
-
})
|
|
592
|
-
});
|
|
593
|
-
}
|
|
594
|
-
|
|
595
86
|
function documentRefsOf(instance) {
|
|
596
87
|
const seen = /* @__PURE__ */ new Map;
|
|
597
88
|
for (const ref of entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
|
|
@@ -985,13 +476,45 @@ function deployedPhrase(definition) {
|
|
|
985
476
|
return ago === "" ? formatShortDateTime(deployedAt) : `${formatShortDateTime(deployedAt)} (${ago})`;
|
|
986
477
|
}
|
|
987
478
|
|
|
988
|
-
const TOOL_PANEL_PADDING = 3, TOOL_HEADING_PADDING = 2;
|
|
479
|
+
const TOOL_PANEL_PADDING = 3, TOOL_HEADING_PADDING = 2, logged = /* @__PURE__ */ new WeakSet;
|
|
989
480
|
|
|
990
|
-
function
|
|
991
|
-
|
|
481
|
+
function logReadFailure(what, error) {
|
|
482
|
+
if (typeof error == "object" && error !== null) {
|
|
483
|
+
if (logged.has(error)) return;
|
|
484
|
+
logged.add(error);
|
|
485
|
+
}
|
|
486
|
+
console.error(`[workflow-studio-plugin] could not load ${what}:`, error);
|
|
992
487
|
}
|
|
993
488
|
|
|
994
|
-
|
|
489
|
+
function useReadFailureLog(what, error) {
|
|
490
|
+
useEffect(() => {
|
|
491
|
+
error !== void 0 && logReadFailure(what, error);
|
|
492
|
+
}, [ what, error ]);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const INSTANCE_CAP = 200, INSTANCES_READ_FAILED = "Couldn’t load workflow data. Try again later.";
|
|
496
|
+
|
|
497
|
+
function useCappedInstances(filter) {
|
|
498
|
+
const {engine: engine} = useWorkflowContext(), capped = useMemo(() => ({
|
|
499
|
+
...filter,
|
|
500
|
+
limit: INSTANCE_CAP
|
|
501
|
+
}), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable, error: error} = useWorkflowInstances({
|
|
502
|
+
engine: engine,
|
|
503
|
+
filter: capped
|
|
504
|
+
});
|
|
505
|
+
return useReadFailureLog("workflow data", error), useMemo(() => {
|
|
506
|
+
const rows = instances ?? [];
|
|
507
|
+
return {
|
|
508
|
+
rows: rows,
|
|
509
|
+
truncated: rows.length + unreadable.length === INSTANCE_CAP,
|
|
510
|
+
loading: loading,
|
|
511
|
+
unreadable: unreadable,
|
|
512
|
+
error: error === void 0 ? void 0 : errorMessage(error)
|
|
513
|
+
};
|
|
514
|
+
}, [ instances, loading, unreadable, error ]);
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
const CATALOG_READ_FAILED = "Couldn’t load the deployed workflows. Try again later.", lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
|
|
995
518
|
|
|
996
519
|
function useDeployedDefinitions() {
|
|
997
520
|
const {engine: engine} = useWorkflowContext(), [definitions, setDefinitions] = useState(() => lastRead.get(engine)?.rows), [error, setError] = useState(void 0);
|
|
@@ -1006,7 +529,7 @@ function useDeployedDefinitions() {
|
|
|
1006
529
|
rows: latest
|
|
1007
530
|
}), cancelled || (setDefinitions(latest), setError(void 0));
|
|
1008
531
|
})().catch(err => {
|
|
1009
|
-
cancelled || setError(describeError(err));
|
|
532
|
+
logReadFailure("the deployed workflows", err), cancelled || setError(describeError(err));
|
|
1010
533
|
}), () => {
|
|
1011
534
|
cancelled = !0;
|
|
1012
535
|
};
|
|
@@ -1016,46 +539,30 @@ function useDeployedDefinitions() {
|
|
|
1016
539
|
};
|
|
1017
540
|
}
|
|
1018
541
|
|
|
1019
|
-
const INSTANCE_CAP = 200;
|
|
1020
|
-
|
|
1021
|
-
function useCappedInstances(filter) {
|
|
1022
|
-
const {engine: engine} = useWorkflowContext(), capped = useMemo(() => ({
|
|
1023
|
-
...filter,
|
|
1024
|
-
limit: INSTANCE_CAP
|
|
1025
|
-
}), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable} = useWorkflowInstances({
|
|
1026
|
-
engine: engine,
|
|
1027
|
-
filter: capped
|
|
1028
|
-
});
|
|
1029
|
-
return useMemo(() => {
|
|
1030
|
-
const rows = instances ?? [];
|
|
1031
|
-
return {
|
|
1032
|
-
rows: rows,
|
|
1033
|
-
truncated: rows.length + unreadable.length === INSTANCE_CAP,
|
|
1034
|
-
loading: loading,
|
|
1035
|
-
unreadable: unreadable
|
|
1036
|
-
};
|
|
1037
|
-
}, [ instances, loading, unreadable ]);
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
542
|
const INSTANCE_CAP_COUNT_NOTE = `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren’t counted.`, NEWEST_INSTANCES = {
|
|
1041
543
|
includeCompleted: !0
|
|
1042
544
|
};
|
|
1043
545
|
|
|
1044
546
|
function useToolInstances() {
|
|
1045
|
-
const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable} = useCappedInstances(NEWEST_INSTANCES);
|
|
547
|
+
const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable, error: error} = useCappedInstances(NEWEST_INSTANCES);
|
|
1046
548
|
return useMemo(() => ({
|
|
1047
549
|
inFlight: rows.filter(instance => terminalState(instance) === "in-flight"),
|
|
1048
550
|
settled: rows.filter(instance => terminalState(instance) !== "in-flight"),
|
|
1049
551
|
truncated: truncated,
|
|
1050
552
|
loading: loading,
|
|
1051
|
-
unreadable: unreadable
|
|
1052
|
-
|
|
553
|
+
unreadable: unreadable,
|
|
554
|
+
error: error
|
|
555
|
+
}), [ rows, truncated, loading, unreadable, error ]);
|
|
1053
556
|
}
|
|
1054
557
|
|
|
1055
558
|
function activeSummary(active) {
|
|
1056
559
|
return active === 0 ? "No active instances" : pluralize("active instance", active, !0);
|
|
1057
560
|
}
|
|
1058
561
|
|
|
562
|
+
function activeHeadline(args) {
|
|
563
|
+
if (args.error === void 0) return args.loading ? "counting active instances…" : activeSummary(args.active);
|
|
564
|
+
}
|
|
565
|
+
|
|
1059
566
|
const CARD_MIN_WIDTH = 280, CARD_RADIUS$1 = 3, CARD_PADDING = 4, CARD_MIN_HEIGHT = 180;
|
|
1060
567
|
|
|
1061
568
|
function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
@@ -1065,13 +572,9 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
|
1065
572
|
titleOf: name => schema.get(name)?.title
|
|
1066
573
|
})
|
|
1067
574
|
});
|
|
1068
|
-
if (error !== void 0) /* @__PURE__ */
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
muted: !0,
|
|
1072
|
-
size: 1,
|
|
1073
|
-
children: catalogReadError(error)
|
|
1074
|
-
})
|
|
575
|
+
if (error !== void 0) /* @__PURE__ */ return jsx(NoteBanner, {
|
|
576
|
+
label: CATALOG_READ_FAILED,
|
|
577
|
+
tone: "critical"
|
|
1075
578
|
});
|
|
1076
579
|
if (definitions === void 0) /* @__PURE__ */
|
|
1077
580
|
return jsx(TabStatusRow, {
|
|
@@ -1098,13 +601,20 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
|
1098
601
|
mappings: mappings,
|
|
1099
602
|
inFlight: instances.inFlight,
|
|
1100
603
|
issues: mappingIssues
|
|
1101
|
-
}), active = rows.reduce((sum, row) => sum + row.activeCount, 0)
|
|
604
|
+
}), active = rows.reduce((sum, row) => sum + row.activeCount, 0), activity = activeHeadline({
|
|
605
|
+
active: active,
|
|
606
|
+
error: instances.error,
|
|
607
|
+
loading: instances.loading
|
|
608
|
+
});
|
|
1102
609
|
/* @__PURE__ */
|
|
1103
610
|
return jsxs(Stack, {
|
|
1104
611
|
gap: 3,
|
|
1105
612
|
paddingBottom: 4,
|
|
1106
613
|
paddingTop: 3,
|
|
1107
|
-
children: [ undeployed,
|
|
614
|
+
children: [ undeployed, instances.error === void 0 ? null : /* @__PURE__ */ jsx(NoteBanner, {
|
|
615
|
+
label: INSTANCES_READ_FAILED,
|
|
616
|
+
tone: "critical"
|
|
617
|
+
}),
|
|
1108
618
|
/* @__PURE__ */ jsxs(Flex, {
|
|
1109
619
|
align: "center",
|
|
1110
620
|
gap: 2,
|
|
@@ -1114,11 +624,10 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
|
1114
624
|
size: 1,
|
|
1115
625
|
weight: "semibold",
|
|
1116
626
|
children: pluralize("workflow", rows.length, !0)
|
|
1117
|
-
}),
|
|
1118
|
-
/* @__PURE__ */ jsx(Text, {
|
|
627
|
+
}), activity === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
1119
628
|
muted: !0,
|
|
1120
629
|
size: 1,
|
|
1121
|
-
children:
|
|
630
|
+
children: activity
|
|
1122
631
|
}) ]
|
|
1123
632
|
}),
|
|
1124
633
|
/* @__PURE__ */ jsx(Grid, {
|
|
@@ -1128,7 +637,7 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
|
1128
637
|
gridTemplateColumns: `repeat(auto-fill, minmax(${CARD_MIN_WIDTH}px, 1fr))`
|
|
1129
638
|
},
|
|
1130
639
|
children: rows.map(row => /* @__PURE__ */ jsx(WorkflowCard, {
|
|
1131
|
-
|
|
640
|
+
countKnown: instances.error === void 0 && !instances.loading,
|
|
1132
641
|
onOpen: () => onOpenWorkflow(row.name),
|
|
1133
642
|
row: row
|
|
1134
643
|
}, row.name))
|
|
@@ -1145,13 +654,13 @@ function UndeployedWorkflows({notices: notices}) {
|
|
|
1145
654
|
/* @__PURE__ */ jsx(Stack, {
|
|
1146
655
|
gap: 2,
|
|
1147
656
|
paddingBottom: 1,
|
|
1148
|
-
children: notices.map(notice => /* @__PURE__ */ jsx(
|
|
657
|
+
children: notices.map(notice => /* @__PURE__ */ jsx(NoteBanner, {
|
|
1149
658
|
label: notice
|
|
1150
659
|
}, notice))
|
|
1151
660
|
});
|
|
1152
661
|
}
|
|
1153
662
|
|
|
1154
|
-
function WorkflowCard({
|
|
663
|
+
function WorkflowCard({countKnown: countKnown, onOpen: onOpen, row: row}) {
|
|
1155
664
|
const flagged = row.brokenBindings.length > 0, card = /* @__PURE__ */ jsx(Card, {
|
|
1156
665
|
as: "button",
|
|
1157
666
|
onClick: onOpen,
|
|
@@ -1186,11 +695,11 @@ function WorkflowCard({countPending: countPending, onOpen: onOpen, row: row}) {
|
|
|
1186
695
|
size: 1,
|
|
1187
696
|
weight: "semibold",
|
|
1188
697
|
children: row.title
|
|
1189
|
-
}),
|
|
698
|
+
}), countKnown ? /* @__PURE__ */ jsx(Text, {
|
|
1190
699
|
muted: !0,
|
|
1191
700
|
size: 1,
|
|
1192
701
|
children: activeSummary(row.activeCount)
|
|
1193
|
-
}) ]
|
|
702
|
+
}) : null ]
|
|
1194
703
|
}) ]
|
|
1195
704
|
})
|
|
1196
705
|
});
|
|
@@ -1244,7 +753,7 @@ function DefinitionPickerPanel({onPick: onPick}) {
|
|
|
1244
753
|
const {definitions: definitions, error: error} = useStartableDefinitions(), [query, setQuery] = useState(""), visible = useMemo(() => {
|
|
1245
754
|
const q = query.trim().toLowerCase();
|
|
1246
755
|
return definitions ? q ? definitions.filter(d => `${d.title} ${d.name}`.toLowerCase().includes(q)) : definitions : [];
|
|
1247
|
-
}, [ definitions, query ]);
|
|
756
|
+
}, [ definitions, query ]), settled = error === void 0 ? definitions : void 0;
|
|
1248
757
|
/* @__PURE__ */
|
|
1249
758
|
return jsx(Box, {
|
|
1250
759
|
padding: 2,
|
|
@@ -1261,13 +770,14 @@ function DefinitionPickerPanel({onPick: onPick}) {
|
|
|
1261
770
|
onChange: event => setQuery(event.currentTarget.value),
|
|
1262
771
|
placeholder: "Filter workflows…",
|
|
1263
772
|
value: query
|
|
1264
|
-
}), error ? /* @__PURE__ */ jsx(
|
|
1265
|
-
|
|
773
|
+
}), error ? /* @__PURE__ */ jsx(NoteBanner, {
|
|
774
|
+
label: CATALOG_READ_FAILED,
|
|
775
|
+
tone: "critical"
|
|
1266
776
|
}) : null, !definitions && !error ? /* @__PURE__ */ jsx(LoadingRow, {
|
|
1267
777
|
label: "Loading workflows…"
|
|
1268
|
-
}) : null,
|
|
778
|
+
}) : null, settled?.length === 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1269
779
|
text: "No startable workflows are deployed"
|
|
1270
|
-
}) : null,
|
|
780
|
+
}) : null, settled !== void 0 && settled.length > 0 && visible.length === 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1271
781
|
text: "No workflows match"
|
|
1272
782
|
}) : null,
|
|
1273
783
|
/* @__PURE__ */ jsx(Stack, {
|
|
@@ -1593,11 +1103,15 @@ function taskRowsOf(args) {
|
|
|
1593
1103
|
open: isOpenActivityStatus(activity.status),
|
|
1594
1104
|
dates: datesOf(fields),
|
|
1595
1105
|
dueDates: dueDatesOf(fields),
|
|
1596
|
-
|
|
1106
|
+
assignees: assigneesOf(fields),
|
|
1597
1107
|
assignedToMe: identity !== void 0 && isActivityAssignedTo({
|
|
1598
1108
|
state: fields,
|
|
1599
1109
|
who: identity
|
|
1600
1110
|
}),
|
|
1111
|
+
roleMatched: identity !== void 0 && isRoleAssignedTo({
|
|
1112
|
+
state: fields,
|
|
1113
|
+
who: identity
|
|
1114
|
+
}),
|
|
1601
1115
|
assignControl: rowAssignControlState(controlArgs),
|
|
1602
1116
|
dateControl: rowDateControlState(controlArgs)
|
|
1603
1117
|
};
|
|
@@ -1680,6 +1194,11 @@ function onlyMyTasks(groups) {
|
|
|
1680
1194
|
});
|
|
1681
1195
|
}
|
|
1682
1196
|
|
|
1197
|
+
function hasRoleMatchedTasks(groups) {
|
|
1198
|
+
const bandHasOne = band => band.rows.some(row => row.roleMatched);
|
|
1199
|
+
return groups.withDocuments.some(group => group.instances.some(bandHasOne)) || groups.withoutDocuments.some(bandHasOne);
|
|
1200
|
+
}
|
|
1201
|
+
|
|
1683
1202
|
function rowActivityEval(entry, row) {
|
|
1684
1203
|
const evaluation = entry?.evaluation;
|
|
1685
1204
|
if (!(evaluation === void 0 || evaluation.currentStage.stage.name !== row.stage)) return evaluation.currentStage.activities.find(a => a.activity.name === row.activityName);
|
|
@@ -1690,7 +1209,7 @@ function taskRowRender(row, entry) {
|
|
|
1690
1209
|
return entry?.evaluation === void 0 || activityEval === void 0 ? {
|
|
1691
1210
|
face: committedRowFace(row),
|
|
1692
1211
|
assignState: row.assignControl,
|
|
1693
|
-
|
|
1212
|
+
assignees: row.assignees,
|
|
1694
1213
|
dateState: row.dateControl,
|
|
1695
1214
|
dueDates: row.dueDates,
|
|
1696
1215
|
terminalActions: [],
|
|
@@ -1823,7 +1342,7 @@ function OrphanedWorkflowsNote({orphans: orphans}) {
|
|
|
1823
1342
|
/* @__PURE__ */
|
|
1824
1343
|
return jsxs(Fragment, {
|
|
1825
1344
|
children: [
|
|
1826
|
-
/* @__PURE__ */ jsx(
|
|
1345
|
+
/* @__PURE__ */ jsx(NoteBanner, {
|
|
1827
1346
|
action: /* @__PURE__ */ jsx(Button, {
|
|
1828
1347
|
fontSize: 1,
|
|
1829
1348
|
mode: "ghost",
|
|
@@ -2127,7 +1646,7 @@ function statusArm(row, statuses) {
|
|
|
2127
1646
|
|
|
2128
1647
|
function assigneeArm(row, assigneeIds) {
|
|
2129
1648
|
const arm = activeArm(assigneeIds);
|
|
2130
|
-
return arm ?
|
|
1649
|
+
return arm ? row.assignees.some(item => item.type === "user" && arm.includes(item.id)) : !0;
|
|
2131
1650
|
}
|
|
2132
1651
|
|
|
2133
1652
|
function rowMatches(args) {
|
|
@@ -2651,6 +2170,55 @@ function FilterRowPortal({filters: filters, onChange: onChange, options: options
|
|
|
2651
2170
|
}), slot) : null;
|
|
2652
2171
|
}
|
|
2653
2172
|
|
|
2173
|
+
function CollapsibleBand({header: header, children: children, background: background = !1, defaultOpen: defaultOpen = !0, onToggle: onToggle, open: controlledOpen, sticky: sticky = !1, title: title}) {
|
|
2174
|
+
const [heldOpen, setHeldOpen] = useState(defaultOpen), open = controlledOpen ?? heldOpen, toggle = onToggle ?? (() => setHeldOpen(value => !value)), verb = open ? "Collapse" : "Expand";
|
|
2175
|
+
/* @__PURE__ */
|
|
2176
|
+
return jsxs(Stack, {
|
|
2177
|
+
gap: 1,
|
|
2178
|
+
children: [
|
|
2179
|
+
/* @__PURE__ */ jsx(Card, {
|
|
2180
|
+
onDoubleClick: event => {
|
|
2181
|
+
event.target instanceof Element && event.target.closest("a, button") || toggle();
|
|
2182
|
+
},
|
|
2183
|
+
padding: 1,
|
|
2184
|
+
radius: 3,
|
|
2185
|
+
style: {
|
|
2186
|
+
userSelect: "none",
|
|
2187
|
+
...sticky ? {
|
|
2188
|
+
position: "sticky",
|
|
2189
|
+
top: 0,
|
|
2190
|
+
zIndex: 1
|
|
2191
|
+
} : {}
|
|
2192
|
+
},
|
|
2193
|
+
tone: background ? "transparent" : "inherit",
|
|
2194
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
2195
|
+
align: "center",
|
|
2196
|
+
children: [
|
|
2197
|
+
/* @__PURE__ */ jsx(Button, {
|
|
2198
|
+
"aria-label": `${verb} ${title}`,
|
|
2199
|
+
fontSize: 1,
|
|
2200
|
+
icon: open ? ChevronDownIcon : ChevronRightIcon,
|
|
2201
|
+
mode: "bleed",
|
|
2202
|
+
onClick: toggle,
|
|
2203
|
+
padding: 2
|
|
2204
|
+
}), header ]
|
|
2205
|
+
})
|
|
2206
|
+
}), open ? children : null ]
|
|
2207
|
+
});
|
|
2208
|
+
}
|
|
2209
|
+
|
|
2210
|
+
function TrailingHairline() {
|
|
2211
|
+
/* @__PURE__ */
|
|
2212
|
+
return jsx(Box, {
|
|
2213
|
+
flex: 1,
|
|
2214
|
+
paddingLeft: 1,
|
|
2215
|
+
paddingRight: 1,
|
|
2216
|
+
children: /* @__PURE__ */ jsx(Hairline, {
|
|
2217
|
+
weight: .5
|
|
2218
|
+
})
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
|
|
2654
2222
|
function isPlainClick(event) {
|
|
2655
2223
|
return !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
|
|
2656
2224
|
}
|
|
@@ -2856,7 +2424,7 @@ function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTer
|
|
|
2856
2424
|
stale: render.stale,
|
|
2857
2425
|
children: /* @__PURE__ */ jsx(ActivityRow, {
|
|
2858
2426
|
assignState: render.assignState,
|
|
2859
|
-
|
|
2427
|
+
assignees: render.assignees,
|
|
2860
2428
|
dateState: render.dateState,
|
|
2861
2429
|
dueDates: render.dueDates,
|
|
2862
2430
|
face: render.face,
|
|
@@ -3110,7 +2678,7 @@ function useOrphanedWorkflows(groups) {
|
|
|
3110
2678
|
}), [ groups, contentResource, missing ]);
|
|
3111
2679
|
}
|
|
3112
2680
|
|
|
3113
|
-
function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
|
|
2681
|
+
function TasksTab({error: error, filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
|
|
3114
2682
|
const mineOnly = tab === "for-me", {binding: binding} = useWorkflowContext(), bands = useBandOpenState({
|
|
3115
2683
|
segment: TASK_BAND_SEGMENTS[tab],
|
|
3116
2684
|
scope: binding.engineResource.id
|
|
@@ -3128,6 +2696,10 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
3128
2696
|
}), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
|
|
3129
2697
|
return orderGroupsByPreviewTitle(cut, titles.titles);
|
|
3130
2698
|
}, [ listed, filters, mineOnly, titles ]);
|
|
2699
|
+
if (error !== void 0) /* @__PURE__ */ return jsx(NoteBanner, {
|
|
2700
|
+
label: INSTANCES_READ_FAILED,
|
|
2701
|
+
tone: "critical"
|
|
2702
|
+
});
|
|
3131
2703
|
if (loading || !revealed) /* @__PURE__ */
|
|
3132
2704
|
return jsx(TabStatusRow, {
|
|
3133
2705
|
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
@@ -3167,7 +2739,7 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
3167
2739
|
}),
|
|
3168
2740
|
/* @__PURE__ */ jsx(OrphanedWorkflowsNote, {
|
|
3169
2741
|
orphans: orphans
|
|
3170
|
-
}), body, truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
2742
|
+
}), mineOnly && hasRoleMatchedTasks(visible) ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, body, truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
3171
2743
|
text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren’t listed.`
|
|
3172
2744
|
}) : null ]
|
|
3173
2745
|
}) ]
|
|
@@ -3545,6 +3117,16 @@ function useBoardInstances(definitionName) {
|
|
|
3545
3117
|
return useCappedInstances(filter);
|
|
3546
3118
|
}
|
|
3547
3119
|
|
|
3120
|
+
function activeCountFace(args) {
|
|
3121
|
+
return args.error !== void 0 || args.loading ? {
|
|
3122
|
+
count: void 0,
|
|
3123
|
+
note: void 0
|
|
3124
|
+
} : {
|
|
3125
|
+
count: args.count,
|
|
3126
|
+
note: args.truncated ? INSTANCE_CAP_COUNT_NOTE : void 0
|
|
3127
|
+
};
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3548
3130
|
function DefinitionPanel({definition: definition}) {
|
|
3549
3131
|
/* @__PURE__ */
|
|
3550
3132
|
return jsx(Box, {
|
|
@@ -3607,6 +3189,23 @@ function SetupIssues({bindings: bindings}) {
|
|
|
3607
3189
|
});
|
|
3608
3190
|
}
|
|
3609
3191
|
|
|
3192
|
+
function ActiveCount({face: face}) {
|
|
3193
|
+
/* @__PURE__ */
|
|
3194
|
+
return jsxs(Stack, {
|
|
3195
|
+
gap: 2,
|
|
3196
|
+
children: [
|
|
3197
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3198
|
+
muted: face.count === void 0,
|
|
3199
|
+
size: 1,
|
|
3200
|
+
children: face.count ?? "—"
|
|
3201
|
+
}), face.note === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
3202
|
+
muted: !0,
|
|
3203
|
+
size: 1,
|
|
3204
|
+
children: face.note
|
|
3205
|
+
}) ]
|
|
3206
|
+
});
|
|
3207
|
+
}
|
|
3208
|
+
|
|
3610
3209
|
function DefinitionFacts({definition: definition}) {
|
|
3611
3210
|
const {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), snapshotWidth = useContainerToken(2), [row] = definitionCatalog({
|
|
3612
3211
|
definitions: [ definition ],
|
|
@@ -3624,6 +3223,12 @@ function DefinitionFacts({definition: definition}) {
|
|
|
3624
3223
|
children: /* @__PURE__ */ jsx(SetupIssues, {
|
|
3625
3224
|
bindings: row.brokenBindings
|
|
3626
3225
|
})
|
|
3226
|
+
}), instances.error === void 0 ? null : /* @__PURE__ */ jsx(Box, {
|
|
3227
|
+
flex: "none",
|
|
3228
|
+
children: /* @__PURE__ */ jsx(NoteBanner, {
|
|
3229
|
+
label: INSTANCES_READ_FAILED,
|
|
3230
|
+
tone: "critical"
|
|
3231
|
+
})
|
|
3627
3232
|
}),
|
|
3628
3233
|
/* @__PURE__ */ jsx(Box, {
|
|
3629
3234
|
flex: "none",
|
|
@@ -3642,21 +3247,13 @@ function DefinitionFacts({definition: definition}) {
|
|
|
3642
3247
|
}),
|
|
3643
3248
|
/* @__PURE__ */ jsx(MetaRow, {
|
|
3644
3249
|
label: "Active instances",
|
|
3645
|
-
children:
|
|
3646
|
-
|
|
3647
|
-
|
|
3648
|
-
|
|
3649
|
-
|
|
3650
|
-
|
|
3651
|
-
|
|
3652
|
-
/* @__PURE__ */ jsx(Text, {
|
|
3653
|
-
size: 1,
|
|
3654
|
-
children: row?.activeCount ?? 0
|
|
3655
|
-
}), instances.truncated ? /* @__PURE__ */ jsx(Text, {
|
|
3656
|
-
muted: !0,
|
|
3657
|
-
size: 1,
|
|
3658
|
-
children: INSTANCE_CAP_COUNT_NOTE
|
|
3659
|
-
}) : null ]
|
|
3250
|
+
children: /* @__PURE__ */ jsx(ActiveCount, {
|
|
3251
|
+
face: activeCountFace({
|
|
3252
|
+
count: row?.activeCount ?? 0,
|
|
3253
|
+
error: instances.error,
|
|
3254
|
+
loading: instances.loading,
|
|
3255
|
+
truncated: instances.truncated
|
|
3256
|
+
})
|
|
3660
3257
|
})
|
|
3661
3258
|
}),
|
|
3662
3259
|
/* @__PURE__ */ jsx(MetaRow, {
|
|
@@ -4193,11 +3790,11 @@ function usePageSelection(page) {
|
|
|
4193
3790
|
}
|
|
4194
3791
|
|
|
4195
3792
|
function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPage, page: page, workflowName: workflowName}) {
|
|
4196
|
-
const {definitions: definitions, error:
|
|
3793
|
+
const {definitions: definitions, error: catalogError} = useDeployedDefinitions(), selection = resolveWorkflowSelection({
|
|
4197
3794
|
definitions: definitions,
|
|
4198
3795
|
routeName: workflowName
|
|
4199
3796
|
}), selected = resolvedDefinition(selection), view = workflowPageView({
|
|
4200
|
-
error:
|
|
3797
|
+
error: catalogError,
|
|
4201
3798
|
page: page,
|
|
4202
3799
|
selection: selection
|
|
4203
3800
|
}), instances = useBoardInstances(view.streamName), {board: board, ready: ready, titles: titles} = useBoard({
|
|
@@ -4209,9 +3806,9 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
|
|
|
4209
3806
|
})), revealed = useRevealGate({
|
|
4210
3807
|
loading: instances.loading,
|
|
4211
3808
|
ready: ready
|
|
4212
|
-
}), waiting = useDelayedFlag(selection.kind === "loading" &&
|
|
3809
|
+
}), waiting = useDelayedFlag(selection.kind === "loading" && catalogError === void 0, WAIT_CUE_DELAY_MS);
|
|
4213
3810
|
if (selection.kind === "loading") /* @__PURE__ */ return jsx(UnresolvedPage, {
|
|
4214
|
-
error:
|
|
3811
|
+
error: catalogError,
|
|
4215
3812
|
waiting: waiting
|
|
4216
3813
|
});
|
|
4217
3814
|
const wiring = {
|
|
@@ -4270,7 +3867,8 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
|
|
|
4270
3867
|
/* @__PURE__ */ jsx(PageBody, {
|
|
4271
3868
|
board: board,
|
|
4272
3869
|
catalogEmpty: (definitions ?? []).length === 0,
|
|
4273
|
-
|
|
3870
|
+
catalogError: catalogError,
|
|
3871
|
+
instancesError: instances.error,
|
|
4274
3872
|
loading: !revealed,
|
|
4275
3873
|
page: page,
|
|
4276
3874
|
selection: selection,
|
|
@@ -4308,8 +3906,8 @@ function UnresolvedPage({error: error, waiting: waiting}) {
|
|
|
4308
3906
|
overflow: "hidden",
|
|
4309
3907
|
children: error === void 0 ? /* @__PURE__ */ jsx(WaitingFace, {
|
|
4310
3908
|
waiting: waiting
|
|
4311
|
-
}) : /* @__PURE__ */ jsx(
|
|
4312
|
-
|
|
3909
|
+
}) : /* @__PURE__ */ jsx(ReadErrorFace, {
|
|
3910
|
+
text: CATALOG_READ_FAILED
|
|
4313
3911
|
})
|
|
4314
3912
|
});
|
|
4315
3913
|
}
|
|
@@ -4325,18 +3923,24 @@ function WaitingFace({waiting: waiting}) {
|
|
|
4325
3923
|
});
|
|
4326
3924
|
}
|
|
4327
3925
|
|
|
4328
|
-
|
|
3926
|
+
const BANNER_LANE_INSET = 2;
|
|
3927
|
+
|
|
3928
|
+
function ReadErrorFace({text: text}) {
|
|
4329
3929
|
/* @__PURE__ */
|
|
4330
3930
|
return jsx(StatusBox, {
|
|
4331
|
-
children: /* @__PURE__ */ jsx(
|
|
4332
|
-
|
|
3931
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
3932
|
+
paddingTop: BANNER_LANE_INSET,
|
|
3933
|
+
children: /* @__PURE__ */ jsx(NoteBanner, {
|
|
3934
|
+
label: text,
|
|
3935
|
+
tone: "critical"
|
|
3936
|
+
})
|
|
4333
3937
|
})
|
|
4334
3938
|
});
|
|
4335
3939
|
}
|
|
4336
3940
|
|
|
4337
|
-
function PageBody({board: board, catalogEmpty: catalogEmpty,
|
|
4338
|
-
if (
|
|
4339
|
-
|
|
3941
|
+
function PageBody({board: board, catalogEmpty: catalogEmpty, catalogError: catalogError, instancesError: instancesError, loading: loading, page: page, selection: selection, wiring: wiring}) {
|
|
3942
|
+
if (catalogError !== void 0) /* @__PURE__ */ return jsx(ReadErrorFace, {
|
|
3943
|
+
text: CATALOG_READ_FAILED
|
|
4340
3944
|
});
|
|
4341
3945
|
if (selection.kind === "unknown") {
|
|
4342
3946
|
const face = unknownWorkflowFace({
|
|
@@ -4354,6 +3958,8 @@ function PageBody({board: board, catalogEmpty: catalogEmpty, error: error, loadi
|
|
|
4354
3958
|
}
|
|
4355
3959
|
return page === "definition" ? /* @__PURE__ */ jsx(DefinitionPanel, {
|
|
4356
3960
|
definition: selection.definition
|
|
3961
|
+
}) : instancesError !== void 0 ? /* @__PURE__ */ jsx(ReadErrorFace, {
|
|
3962
|
+
text: INSTANCES_READ_FAILED
|
|
4357
3963
|
}) : loading || board === void 0 ? /* @__PURE__ */ jsx(StatusBox, {
|
|
4358
3964
|
children: /* @__PURE__ */ jsx(Box, {
|
|
4359
3965
|
paddingTop: LANE_PADDING,
|
|
@@ -4520,6 +4126,7 @@ function HomePanel({filterSlot: filterSlot, identity: identity, instances: insta
|
|
|
4520
4126
|
return tab === "overview" ? /* @__PURE__ */ jsx(OverviewTab, {
|
|
4521
4127
|
onOpenWorkflow: onOpenWorkflow
|
|
4522
4128
|
}) : /* @__PURE__ */ jsx(TasksTab, {
|
|
4129
|
+
error: instances.error,
|
|
4523
4130
|
filterSlot: filterSlot,
|
|
4524
4131
|
identity: identity,
|
|
4525
4132
|
instances: [ ...instances.inFlight, ...instances.settled ],
|