@sanity/workflow-studio-plugin 0.26.0 → 0.27.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 +109 -0
- package/dist/_chunks-cjs/index.cjs +1190 -545
- package/dist/_chunks-cjs/workflows-tool-root.cjs +61 -517
- package/dist/_chunks-es/index.js +1203 -538
- package/dist/_chunks-es/workflows-tool-root.js +60 -520
- package/package.json +12 -12
|
@@ -1,20 +1,14 @@
|
|
|
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 {
|
|
12
|
-
|
|
13
|
-
import { ClockIcon } from "@sanity/icons/Clock";
|
|
14
|
-
|
|
15
|
-
import { CogIcon } from "@sanity/icons/Cog";
|
|
16
|
-
|
|
17
|
-
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
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, EmptyState, CautionNote, HoverHint, DismissablePopover, createSubscribers, useDelayedFlag, definitionSnapshotOf, findActivity, stageTitle, stateByActivity, findActivityNode, rowDateControlState, rowAssignControlState, 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, 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";
|
|
18
12
|
|
|
19
13
|
import { entryDocRefs, startKindOf, isSubjectEntry, isInputSourced, latestDeployedDefinitions, terminalState, isStartableDefinition, toBareId, findOpenStageEntry } from "@sanity/workflow-engine";
|
|
20
14
|
|
|
@@ -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);
|
|
@@ -1593,7 +1084,7 @@ function taskRowsOf(args) {
|
|
|
1593
1084
|
open: isOpenActivityStatus(activity.status),
|
|
1594
1085
|
dates: datesOf(fields),
|
|
1595
1086
|
dueDates: dueDatesOf(fields),
|
|
1596
|
-
|
|
1087
|
+
assignees: assigneesOf(fields),
|
|
1597
1088
|
assignedToMe: identity !== void 0 && isActivityAssignedTo({
|
|
1598
1089
|
state: fields,
|
|
1599
1090
|
who: identity
|
|
@@ -1690,7 +1181,7 @@ function taskRowRender(row, entry) {
|
|
|
1690
1181
|
return entry?.evaluation === void 0 || activityEval === void 0 ? {
|
|
1691
1182
|
face: committedRowFace(row),
|
|
1692
1183
|
assignState: row.assignControl,
|
|
1693
|
-
|
|
1184
|
+
assignees: row.assignees,
|
|
1694
1185
|
dateState: row.dateControl,
|
|
1695
1186
|
dueDates: row.dueDates,
|
|
1696
1187
|
terminalActions: [],
|
|
@@ -2127,7 +1618,7 @@ function statusArm(row, statuses) {
|
|
|
2127
1618
|
|
|
2128
1619
|
function assigneeArm(row, assigneeIds) {
|
|
2129
1620
|
const arm = activeArm(assigneeIds);
|
|
2130
|
-
return arm ?
|
|
1621
|
+
return arm ? row.assignees.some(item => item.type === "user" && arm.includes(item.id)) : !0;
|
|
2131
1622
|
}
|
|
2132
1623
|
|
|
2133
1624
|
function rowMatches(args) {
|
|
@@ -2651,6 +2142,55 @@ function FilterRowPortal({filters: filters, onChange: onChange, options: options
|
|
|
2651
2142
|
}), slot) : null;
|
|
2652
2143
|
}
|
|
2653
2144
|
|
|
2145
|
+
function CollapsibleBand({header: header, children: children, background: background = !1, defaultOpen: defaultOpen = !0, onToggle: onToggle, open: controlledOpen, sticky: sticky = !1, title: title}) {
|
|
2146
|
+
const [heldOpen, setHeldOpen] = useState(defaultOpen), open = controlledOpen ?? heldOpen, toggle = onToggle ?? (() => setHeldOpen(value => !value)), verb = open ? "Collapse" : "Expand";
|
|
2147
|
+
/* @__PURE__ */
|
|
2148
|
+
return jsxs(Stack, {
|
|
2149
|
+
gap: 1,
|
|
2150
|
+
children: [
|
|
2151
|
+
/* @__PURE__ */ jsx(Card, {
|
|
2152
|
+
onDoubleClick: event => {
|
|
2153
|
+
event.target instanceof Element && event.target.closest("a, button") || toggle();
|
|
2154
|
+
},
|
|
2155
|
+
padding: 1,
|
|
2156
|
+
radius: 3,
|
|
2157
|
+
style: {
|
|
2158
|
+
userSelect: "none",
|
|
2159
|
+
...sticky ? {
|
|
2160
|
+
position: "sticky",
|
|
2161
|
+
top: 0,
|
|
2162
|
+
zIndex: 1
|
|
2163
|
+
} : {}
|
|
2164
|
+
},
|
|
2165
|
+
tone: background ? "transparent" : "inherit",
|
|
2166
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
2167
|
+
align: "center",
|
|
2168
|
+
children: [
|
|
2169
|
+
/* @__PURE__ */ jsx(Button, {
|
|
2170
|
+
"aria-label": `${verb} ${title}`,
|
|
2171
|
+
fontSize: 1,
|
|
2172
|
+
icon: open ? ChevronDownIcon : ChevronRightIcon,
|
|
2173
|
+
mode: "bleed",
|
|
2174
|
+
onClick: toggle,
|
|
2175
|
+
padding: 2
|
|
2176
|
+
}), header ]
|
|
2177
|
+
})
|
|
2178
|
+
}), open ? children : null ]
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
function TrailingHairline() {
|
|
2183
|
+
/* @__PURE__ */
|
|
2184
|
+
return jsx(Box, {
|
|
2185
|
+
flex: 1,
|
|
2186
|
+
paddingLeft: 1,
|
|
2187
|
+
paddingRight: 1,
|
|
2188
|
+
children: /* @__PURE__ */ jsx(Hairline, {
|
|
2189
|
+
weight: .5
|
|
2190
|
+
})
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
|
|
2654
2194
|
function isPlainClick(event) {
|
|
2655
2195
|
return !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
|
|
2656
2196
|
}
|
|
@@ -2856,7 +2396,7 @@ function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTer
|
|
|
2856
2396
|
stale: render.stale,
|
|
2857
2397
|
children: /* @__PURE__ */ jsx(ActivityRow, {
|
|
2858
2398
|
assignState: render.assignState,
|
|
2859
|
-
|
|
2399
|
+
assignees: render.assignees,
|
|
2860
2400
|
dateState: render.dateState,
|
|
2861
2401
|
dueDates: render.dueDates,
|
|
2862
2402
|
face: render.face,
|