@sanity/workflow-studio-plugin 0.22.0 → 0.24.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 +171 -0
- package/README.md +13 -20
- package/dist/_chunks-cjs/index.cjs +3190 -2874
- package/dist/_chunks-cjs/workflows-tool-root.cjs +2776 -668
- package/dist/_chunks-es/index.js +3100 -2844
- package/dist/_chunks-es/workflows-tool-root.js +2803 -681
- package/package.json +12 -12
|
@@ -1,26 +1,52 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Box, Text, Card, Flex, Stack, MenuButton, Menu, MenuItem, Button, Badge, Dialog, TextInput, useTheme_v2, TabPanel } from "@sanity/ui";
|
|
4
4
|
|
|
5
|
-
import { useWorkflowTelemetry
|
|
5
|
+
import { useWorkflowTelemetry } from "@sanity/workflow-react";
|
|
6
6
|
|
|
7
|
-
import { useState, useEffect,
|
|
7
|
+
import { useMemo, useSyncExternalStore, useCallback, useState, useEffect, useRef } from "react";
|
|
8
8
|
|
|
9
9
|
import { useRouter } from "sanity/router";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { createSubscribers, useWorkflowContext, readDeployedDefinitions, describeError, useDelayedFlag, instanceTitle, definitionSnapshotOf, stageTitle, DocPreviewLink, NOTICE_PADDING, chipPadding, CountedLabel, useSpaceToken, useRadiusToken, HoverHint, definitionFingerprint, CollapsibleBand, gdrLocality, openableSchemaType, WORKFLOW_API_VERSION, WorkflowTitleSeedDrifted, WorkflowBoardLayoutChanged, UnreadableDocsNote, LogEventOnMount, WorkflowBoardWorkflowSelected, LoadingRow, EmptyState, mappingIssueDetail, formatTimeAgo, formatShortDateTime, namesNoDeployedDefinition, SpinnerSlot, WorkflowDefinitionDetailViewed, useContainerToken, MetaRow, CautionNote, useBadgeCapTrim, TOAST_ID, useDefinition, useWorkflowToast, isLiveEntry, UserAvatar, formatShortAgo, formatDateTime, useAvatarSize, HintedMenuButton, useUserDisplay, useWorkflowInstanceEntry, useLogEventOnMount, WorkflowInstanceDetailViewed, InvalidDocNotice, openActivityGone, isEvaluationStale, InstanceSnapshotBody, ActivityDetailDialog, instanceBreadcrumb, DismissablePopover, findActivity, stateByActivity, findActivityNode, rowDateControlState, rowAssignControlState, isActivityAssignedTo, assigneeUserIdsOf, isOpenActivityStatus, isDateFieldKind, committedRowFace, activityRowProps, formatDate, parseStoredDateValue, useUserDisplays, useProjectMembers, WorkflowTaskFiltersApplied, DocRefFace, useEditField, useSaveField, dateControlValue, stopRowMouseDown, stopRowClick, dateControlKind, parseDateFieldValue, hasTimeOfDay, serializeDateFieldValue, LinkChip, BreadcrumbTail, StageFace, TrailingHairline, StaleLock, ActivityRow, ForMeEmptyState, pathSegmentTab, TabSwitch, WorkflowToolOpened, useAssignmentIdentity } from "./index.js";
|
|
12
12
|
|
|
13
|
-
import {
|
|
13
|
+
import { SearchIcon } from "@sanity/icons/Search";
|
|
14
|
+
|
|
15
|
+
import { latestDeployedDefinitions, entryDocRefs, terminalState, startKindOf, isSubjectEntry, isInputSourced, isStartableDefinition, toBareId, findOpenStageEntry } from "@sanity/workflow-engine";
|
|
16
|
+
|
|
17
|
+
import { ChevronDownIcon } from "@sanity/icons/ChevronDown";
|
|
18
|
+
|
|
19
|
+
import { InlineIcon } from "@sanity/icons/Inline";
|
|
20
|
+
|
|
21
|
+
import { StackCompactIcon } from "@sanity/icons/StackCompact";
|
|
22
|
+
|
|
23
|
+
import { createPortal } from "react-dom";
|
|
24
|
+
|
|
25
|
+
import { isReferenceSchemaType } from "@sanity/types";
|
|
26
|
+
|
|
27
|
+
import { useDocumentPreviewStore, useSchema, usePerspective, useClient } from "sanity";
|
|
28
|
+
|
|
29
|
+
import { useWorkflowInstances } from "@sanity/workflow-studio";
|
|
30
|
+
|
|
31
|
+
import { WarningOutlineIcon } from "@sanity/icons/WarningOutline";
|
|
14
32
|
|
|
15
33
|
import { WorkflowDiagram } from "@sanity/workflow-diagram";
|
|
16
34
|
|
|
17
|
-
import {
|
|
35
|
+
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
18
36
|
|
|
19
|
-
import {
|
|
37
|
+
import { InfoOutlineIcon } from "@sanity/icons/InfoOutline";
|
|
20
38
|
|
|
21
|
-
import {
|
|
39
|
+
import { TransferIcon } from "@sanity/icons/Transfer";
|
|
22
40
|
|
|
23
|
-
import
|
|
41
|
+
import pluralize from "pluralize-esm";
|
|
42
|
+
|
|
43
|
+
import { ClockIcon } from "@sanity/icons/Clock";
|
|
44
|
+
|
|
45
|
+
import { CogIcon } from "@sanity/icons/Cog";
|
|
46
|
+
|
|
47
|
+
import { EllipsisHorizontalIcon } from "@sanity/icons/EllipsisHorizontal";
|
|
48
|
+
|
|
49
|
+
import { AddIcon } from "@sanity/icons/Add";
|
|
24
50
|
|
|
25
51
|
import { CheckmarkIcon } from "@sanity/icons/Checkmark";
|
|
26
52
|
|
|
@@ -32,88 +58,2369 @@ import { MemberAvatar, DatePicker, ClearableDatePicker } from "@sanity/workflow-
|
|
|
32
58
|
|
|
33
59
|
import { endOfDay } from "date-fns/endOfDay";
|
|
34
60
|
|
|
35
|
-
import { createPortal } from "react-dom";
|
|
36
|
-
|
|
37
61
|
import { CalendarIcon } from "@sanity/icons/Calendar";
|
|
38
62
|
|
|
39
|
-
|
|
63
|
+
function MutedNote({text: text}) {
|
|
64
|
+
/* @__PURE__ */
|
|
65
|
+
return jsx(Box, {
|
|
66
|
+
paddingY: 3,
|
|
67
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
68
|
+
muted: !0,
|
|
69
|
+
size: 1,
|
|
70
|
+
children: text
|
|
71
|
+
})
|
|
72
|
+
});
|
|
73
|
+
}
|
|
40
74
|
|
|
41
|
-
function
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
function TabStatusRow({children: children}) {
|
|
76
|
+
/* @__PURE__ */
|
|
77
|
+
return jsx(Box, {
|
|
78
|
+
paddingLeft: 2,
|
|
79
|
+
paddingY: 3,
|
|
80
|
+
children: children
|
|
81
|
+
});
|
|
45
82
|
}
|
|
46
83
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
84
|
+
const TOOL_PANEL_PADDING = 3, UNREADABLE = {
|
|
85
|
+
readable: !1,
|
|
86
|
+
raw: void 0
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
function defaultStorage() {
|
|
90
|
+
try {
|
|
91
|
+
return globalThis.localStorage;
|
|
92
|
+
} catch {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function readItem(storage, key) {
|
|
98
|
+
if (!storage) return UNREADABLE;
|
|
99
|
+
try {
|
|
100
|
+
const raw = storage.getItem(key);
|
|
101
|
+
return {
|
|
102
|
+
readable: !0,
|
|
103
|
+
raw: raw === null ? void 0 : raw
|
|
104
|
+
};
|
|
105
|
+
} catch {
|
|
106
|
+
return UNREADABLE;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function writeItem(args) {
|
|
111
|
+
if (!args.storage) return !1;
|
|
112
|
+
try {
|
|
113
|
+
return args.storage.setItem(args.key, args.value), !0;
|
|
114
|
+
} catch {
|
|
115
|
+
return !1;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const BAND_DEFAULT_OPEN = !0, UNGROUPED_BAND_KEY = "section:no-document", PAYLOAD_VERSION = 2, MAX_ENTRIES = 200;
|
|
120
|
+
|
|
121
|
+
function bandStateStorageKey(scope) {
|
|
122
|
+
return `sanity.workflows.tool.bands:${scope}`;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function isRecord(value) {
|
|
126
|
+
return typeof value == "object" && value !== null && !Array.isArray(value);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function bandEntryOf(value) {
|
|
130
|
+
if (!isRecord(value)) return;
|
|
131
|
+
const {open: open, touched: touched} = value;
|
|
132
|
+
if (!(typeof open != "boolean" || typeof touched != "number") && Number.isFinite(touched)) return {
|
|
133
|
+
open: open,
|
|
134
|
+
touched: touched
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function parsePayload(raw) {
|
|
139
|
+
try {
|
|
140
|
+
const parsed = JSON.parse(raw);
|
|
141
|
+
if (!isRecord(parsed) || parsed.version !== PAYLOAD_VERSION || !isRecord(parsed.bands)) return;
|
|
142
|
+
const entries = [];
|
|
143
|
+
for (const [key, value] of Object.entries(parsed.bands)) {
|
|
144
|
+
const entry = bandEntryOf(value);
|
|
145
|
+
if (entry === void 0) return;
|
|
146
|
+
entries.push([ key, entry ]);
|
|
147
|
+
}
|
|
148
|
+
return Object.fromEntries(entries);
|
|
149
|
+
} catch {
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function readStored(storage, storageKey) {
|
|
155
|
+
const {readable: readable, raw: raw} = readItem(storage, storageKey);
|
|
156
|
+
if (!readable) return {
|
|
157
|
+
entries: void 0,
|
|
158
|
+
corrupt: !1
|
|
159
|
+
};
|
|
160
|
+
if (raw === void 0) return {
|
|
161
|
+
entries: {},
|
|
162
|
+
corrupt: !1
|
|
163
|
+
};
|
|
164
|
+
const entries = parsePayload(raw);
|
|
165
|
+
return {
|
|
166
|
+
entries: entries,
|
|
167
|
+
corrupt: entries === void 0
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function writeStored(args) {
|
|
172
|
+
return writeItem({
|
|
173
|
+
storage: args.storage,
|
|
174
|
+
key: args.storageKey,
|
|
175
|
+
value: JSON.stringify({
|
|
176
|
+
version: PAYLOAD_VERSION,
|
|
177
|
+
bands: args.entries
|
|
60
178
|
})
|
|
61
179
|
});
|
|
62
180
|
}
|
|
63
181
|
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
182
|
+
function baseEntries(args) {
|
|
183
|
+
return args.stored === void 0 ? args.held : args.unpersisted ? mergeByTouched(args.stored, args.held) : args.stored;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function mergeByTouched(stored, held) {
|
|
187
|
+
const merged = {
|
|
188
|
+
...stored
|
|
189
|
+
};
|
|
190
|
+
for (const [key, entry] of Object.entries(held)) {
|
|
191
|
+
const persisted = merged[key];
|
|
192
|
+
(persisted === void 0 || entry.touched >= persisted.touched) && (merged[key] = entry);
|
|
193
|
+
}
|
|
194
|
+
return merged;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function prune(entries) {
|
|
198
|
+
const held = Object.entries(entries);
|
|
199
|
+
return held.length <= MAX_ENTRIES ? entries : Object.fromEntries(held.sort(([, a], [, b]) => b.touched - a.touched).slice(0, MAX_ENTRIES));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function togglesOf(entries) {
|
|
203
|
+
return new Map(Object.entries(entries).map(([key, entry]) => [ key, entry.open ]));
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function createBandStateStore(args) {
|
|
207
|
+
const storage = args.storage ?? defaultStorage(), initial = readStored(storage, args.storageKey);
|
|
208
|
+
initial.corrupt && writeStored({
|
|
209
|
+
storage: storage,
|
|
210
|
+
storageKey: args.storageKey,
|
|
211
|
+
entries: {}
|
|
68
212
|
});
|
|
69
|
-
|
|
213
|
+
let held = initial.entries ?? {}, toggles = togglesOf(held);
|
|
214
|
+
const subscribers = createSubscribers();
|
|
215
|
+
let unpersisted = !1;
|
|
216
|
+
return {
|
|
217
|
+
subscribe: subscribers.subscribe,
|
|
218
|
+
read: () => toggles,
|
|
219
|
+
setOpen(key, open) {
|
|
220
|
+
const stored = readStored(storage, args.storageKey).entries, base = baseEntries({
|
|
221
|
+
stored: stored,
|
|
222
|
+
held: held,
|
|
223
|
+
unpersisted: unpersisted
|
|
224
|
+
});
|
|
225
|
+
held = prune({
|
|
226
|
+
...base,
|
|
227
|
+
[key]: {
|
|
228
|
+
open: open,
|
|
229
|
+
touched: Date.now()
|
|
230
|
+
}
|
|
231
|
+
}), toggles = togglesOf(held), unpersisted = !writeStored({
|
|
232
|
+
storage: storage,
|
|
233
|
+
storageKey: args.storageKey,
|
|
234
|
+
entries: held
|
|
235
|
+
}), subscribers.notify();
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function useBandOpenState(args) {
|
|
241
|
+
const {segment: segment, scope: scope} = args, store = useMemo(() => createBandStateStore({
|
|
242
|
+
storageKey: bandStateStorageKey(scope)
|
|
243
|
+
}), [ scope ]);
|
|
244
|
+
return useMemo(() => ({
|
|
245
|
+
segment: segment,
|
|
246
|
+
store: store
|
|
247
|
+
}), [ segment, store ]);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
function openOf(store, qualified) {
|
|
251
|
+
return store.read().get(qualified) ?? BAND_DEFAULT_OPEN;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function useBandOpen(bands, key) {
|
|
255
|
+
const {segment: segment, store: store} = bands, qualified = `${segment}/${key}`;
|
|
256
|
+
return {
|
|
257
|
+
open: useSyncExternalStore(store.subscribe, useCallback(() => openOf(store, qualified), [ store, qualified ])),
|
|
258
|
+
toggle: () => store.setOpen(qualified, !openOf(store, qualified))
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
|
|
263
|
+
|
|
264
|
+
function useDeployedDefinitions() {
|
|
265
|
+
const {engine: engine} = useWorkflowContext(), [definitions, setDefinitions] = useState(() => lastRead.get(engine)?.rows), [error, setError] = useState(void 0);
|
|
70
266
|
return useEffect(() => {
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
267
|
+
let cancelled = !1;
|
|
268
|
+
setDefinitions(lastRead.get(engine)?.rows), setError(void 0);
|
|
269
|
+
const seq = (issuedReads.get(engine) ?? 0) + 1;
|
|
270
|
+
return issuedReads.set(engine, seq), (async () => {
|
|
271
|
+
const rows = await readDeployedDefinitions(engine), latest = latestDeployedDefinitions(rows), held = lastRead.get(engine);
|
|
272
|
+
(held === void 0 || seq > held.seq) && lastRead.set(engine, {
|
|
273
|
+
seq: seq,
|
|
274
|
+
rows: latest
|
|
275
|
+
}), cancelled || (setDefinitions(latest), setError(void 0));
|
|
276
|
+
})().catch(err => {
|
|
277
|
+
cancelled || setError(describeError(err));
|
|
278
|
+
}), () => {
|
|
279
|
+
cancelled = !0;
|
|
280
|
+
};
|
|
281
|
+
}, [ engine ]), {
|
|
282
|
+
definitions: definitions,
|
|
283
|
+
error: error
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const REVEAL_MAX_WAIT_MS = 2e3;
|
|
288
|
+
|
|
289
|
+
function useRevealGate(args) {
|
|
290
|
+
const {loading: loading, ready: ready} = args, settled = !loading && ready, expired = useDelayedFlag(!loading && !ready, REVEAL_MAX_WAIT_MS), [revealed, setRevealed] = useState(!1);
|
|
291
|
+
return useEffect(() => {
|
|
292
|
+
!revealed && (settled || expired) && setRevealed(!0);
|
|
293
|
+
}, [ expired, revealed, settled ]), revealed || settled || expired;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function documentRefsOf(instance) {
|
|
297
|
+
const seen = /* @__PURE__ */ new Map;
|
|
298
|
+
for (const ref of entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
|
|
299
|
+
return [ ...seen.values() ];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function byTitleThenName(a, b) {
|
|
303
|
+
return a.title.localeCompare(b.title) || a.name.localeCompare(b.name);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
function byResolvedTitle(titleOf, keyOf) {
|
|
307
|
+
return (a, b) => {
|
|
308
|
+
const aTitle = titleOf(a), bTitle = titleOf(b);
|
|
309
|
+
return aTitle !== void 0 && bTitle !== void 0 ? aTitle.localeCompare(bTitle) || keyOf(a).localeCompare(keyOf(b)) : aTitle !== void 0 ? -1 : bTitle !== void 0 ? 1 : keyOf(a).localeCompare(keyOf(b));
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const BOARD_EMPTY_STAGE_NOTE = "No items";
|
|
314
|
+
|
|
315
|
+
function faceOf(instance) {
|
|
316
|
+
const document = documentRefsOf(instance)[0];
|
|
317
|
+
return document ? {
|
|
318
|
+
kind: "document",
|
|
319
|
+
document: document
|
|
320
|
+
} : {
|
|
321
|
+
kind: "instance",
|
|
322
|
+
title: instanceTitle(instance, definitionSnapshotOf(instance))
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function boardRuns(instances) {
|
|
327
|
+
return instances.filter(instance => terminalState(instance) !== "aborted");
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function groupsOf(args) {
|
|
331
|
+
const declared = args.definition.stages.map(stage => stage.name), retired = [ ...args.byStage.keys() ].filter(stage => !declared.includes(stage)).sort();
|
|
332
|
+
return [ ...declared, ...retired ].map(stage => ({
|
|
333
|
+
stage: stage,
|
|
334
|
+
title: stageTitle(args.definition, stage),
|
|
335
|
+
cards: args.byStage.get(stage) ?? [],
|
|
336
|
+
retired: !declared.includes(stage)
|
|
337
|
+
}));
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function deriveBoard(args) {
|
|
341
|
+
const byStage = /* @__PURE__ */ new Map;
|
|
342
|
+
for (const instance of boardRuns(args.instances)) {
|
|
343
|
+
const card = {
|
|
344
|
+
instanceId: instance._id,
|
|
345
|
+
face: faceOf(instance)
|
|
346
|
+
}, held = byStage.get(instance.currentStage);
|
|
347
|
+
held ? held.push(card) : byStage.set(instance.currentStage, [ card ]);
|
|
348
|
+
}
|
|
349
|
+
return {
|
|
350
|
+
groups: groupsOf({
|
|
351
|
+
definition: args.definition,
|
|
352
|
+
byStage: byStage
|
|
77
353
|
})
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function orderBoardByPreviewTitle(board, titles) {
|
|
358
|
+
const compare = byResolvedTitle(card => card.face.kind === "instance" ? card.face.title : titles.get(card.face.document.id), card => card.instanceId);
|
|
359
|
+
return {
|
|
360
|
+
groups: board.groups.map(group => ({
|
|
361
|
+
...group,
|
|
362
|
+
cards: [ ...group.cards ].sort(compare)
|
|
363
|
+
}))
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
function boardDocuments(board) {
|
|
368
|
+
return board.groups.flatMap(group => group.cards.flatMap(card => card.face.kind === "document" ? [ card.face.document ] : []));
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
function BoardCard({card: card, size: size}) {
|
|
372
|
+
return size === "slim" ? card.face.kind === "instance" ? /* @__PURE__ */ jsx(InstanceFace, {
|
|
373
|
+
size: size,
|
|
374
|
+
title: card.face.title
|
|
375
|
+
}) : /* @__PURE__ */ jsx(DocPreviewLink, {
|
|
376
|
+
gdr: card.face.document,
|
|
377
|
+
layout: "row",
|
|
378
|
+
source: "board-card"
|
|
379
|
+
}) : /* @__PURE__ */ jsx(Card, {
|
|
380
|
+
border: !0,
|
|
381
|
+
radius: 3,
|
|
382
|
+
children: card.face.kind === "instance" ? /* @__PURE__ */ jsx(InstanceFace, {
|
|
383
|
+
size: size,
|
|
384
|
+
title: card.face.title
|
|
385
|
+
}) : /* @__PURE__ */ jsx(DocPreviewLink, {
|
|
386
|
+
gdr: card.face.document,
|
|
387
|
+
layout: "bare",
|
|
388
|
+
source: "board-card"
|
|
88
389
|
})
|
|
89
390
|
});
|
|
90
391
|
}
|
|
91
392
|
|
|
92
|
-
function
|
|
393
|
+
function InstanceFace({size: size, title: title}) {
|
|
394
|
+
const fill = size === "slim";
|
|
93
395
|
/* @__PURE__ */
|
|
94
|
-
return
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
396
|
+
return jsx(Box, {
|
|
397
|
+
padding: fill ? chipPadding(!0) : NOTICE_PADDING,
|
|
398
|
+
style: fill ? {
|
|
399
|
+
width: "100%"
|
|
400
|
+
} : {},
|
|
401
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
99
402
|
size: 1,
|
|
100
|
-
|
|
403
|
+
textOverflow: "ellipsis",
|
|
404
|
+
weight: "medium",
|
|
405
|
+
children: title
|
|
406
|
+
})
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function StageGroupHeading({group: group}) {
|
|
411
|
+
/* @__PURE__ */
|
|
412
|
+
return jsxs(Flex, {
|
|
413
|
+
align: "center",
|
|
414
|
+
gap: 2,
|
|
415
|
+
children: [
|
|
416
|
+
/* @__PURE__ */ jsx(CountedLabel, {
|
|
417
|
+
count: group.cards.length,
|
|
418
|
+
label: /* @__PURE__ */ jsx(Text, {
|
|
419
|
+
size: 1,
|
|
420
|
+
weight: "medium",
|
|
421
|
+
children: group.title
|
|
422
|
+
})
|
|
423
|
+
}), group.retired ?
|
|
424
|
+
/* @__PURE__ */ jsx(Text, {
|
|
425
|
+
muted: !0,
|
|
426
|
+
size: 1,
|
|
427
|
+
children: "No longer in this workflow"
|
|
428
|
+
}) : null ]
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const COLUMN_WIDTH = 320, PANEL_FADE = "linear-gradient(to bottom, rgb(0 0 0) 50%, rgb(0 0 0 / 0) 75%)";
|
|
433
|
+
|
|
434
|
+
function StageColumn({group: group}) {
|
|
435
|
+
/* @__PURE__ */
|
|
436
|
+
return jsxs(Box, {
|
|
437
|
+
flex: "none",
|
|
438
|
+
style: {
|
|
439
|
+
display: "flex",
|
|
440
|
+
flexDirection: "column",
|
|
441
|
+
position: "relative",
|
|
442
|
+
width: COLUMN_WIDTH
|
|
443
|
+
},
|
|
444
|
+
children: [
|
|
445
|
+
/* @__PURE__ */ jsx(Card, {
|
|
446
|
+
"aria-hidden": !0,
|
|
447
|
+
radius: 3,
|
|
448
|
+
style: {
|
|
449
|
+
inset: 0,
|
|
450
|
+
maskImage: PANEL_FADE,
|
|
451
|
+
position: "absolute",
|
|
452
|
+
WebkitMaskImage: PANEL_FADE
|
|
453
|
+
},
|
|
454
|
+
tone: "transparent"
|
|
455
|
+
}),
|
|
456
|
+
/* @__PURE__ */ jsx(Box, {
|
|
457
|
+
flex: "none",
|
|
458
|
+
paddingX: 3,
|
|
459
|
+
paddingY: 4,
|
|
460
|
+
style: {
|
|
461
|
+
position: "relative"
|
|
462
|
+
},
|
|
463
|
+
children: /* @__PURE__ */ jsx(StageGroupHeading, {
|
|
464
|
+
group: group
|
|
465
|
+
})
|
|
466
|
+
}), group.cards.length === 0 ?
|
|
467
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
468
|
+
align: "center",
|
|
469
|
+
flex: 1,
|
|
470
|
+
justify: "center",
|
|
471
|
+
padding: 2,
|
|
472
|
+
style: {
|
|
473
|
+
position: "relative"
|
|
474
|
+
},
|
|
475
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
476
|
+
muted: !0,
|
|
477
|
+
size: 1,
|
|
478
|
+
children: BOARD_EMPTY_STAGE_NOTE
|
|
479
|
+
})
|
|
480
|
+
}) :
|
|
481
|
+
/* @__PURE__ */ jsx(Box, {
|
|
482
|
+
flex: 1,
|
|
483
|
+
overflow: "auto",
|
|
484
|
+
paddingBottom: 2,
|
|
485
|
+
paddingX: 2,
|
|
486
|
+
style: {
|
|
487
|
+
minHeight: 0,
|
|
488
|
+
position: "relative"
|
|
489
|
+
},
|
|
490
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
491
|
+
gap: 1,
|
|
492
|
+
children: group.cards.map(card => /* @__PURE__ */ jsx(BoardCard, {
|
|
493
|
+
card: card,
|
|
494
|
+
size: "block"
|
|
495
|
+
}, card.instanceId))
|
|
496
|
+
})
|
|
497
|
+
}) ]
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function StageGroups({board: board}) {
|
|
502
|
+
const topGap = useSpaceToken(TOOL_PANEL_PADDING);
|
|
503
|
+
/* @__PURE__ */
|
|
504
|
+
return jsx(Box, {
|
|
505
|
+
flex: 1,
|
|
506
|
+
style: {
|
|
507
|
+
minHeight: 0,
|
|
508
|
+
position: "relative"
|
|
509
|
+
},
|
|
510
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
511
|
+
style: {
|
|
512
|
+
bottom: 0,
|
|
513
|
+
left: 0,
|
|
514
|
+
overflowX: "auto",
|
|
515
|
+
overflowY: "hidden",
|
|
516
|
+
position: "absolute",
|
|
517
|
+
right: 0,
|
|
518
|
+
top: topGap
|
|
519
|
+
},
|
|
520
|
+
children: /* @__PURE__ */ jsx(Flex, {
|
|
521
|
+
align: "stretch",
|
|
522
|
+
gap: 2,
|
|
523
|
+
paddingX: TOOL_PANEL_PADDING,
|
|
524
|
+
style: {
|
|
525
|
+
boxSizing: "border-box",
|
|
526
|
+
height: "100%",
|
|
527
|
+
width: "max-content"
|
|
528
|
+
},
|
|
529
|
+
children: board.groups.map(group => /* @__PURE__ */ jsx(StageColumn, {
|
|
530
|
+
group: group
|
|
531
|
+
}, group.stage))
|
|
532
|
+
})
|
|
533
|
+
})
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function isPlainClick(event) {
|
|
538
|
+
return !event.altKey && !event.ctrlKey && !event.metaKey && !event.shiftKey;
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
const BOARD_LAYOUTS = [ "columns", "stack" ], BOARD_DEFAULT_LAYOUT = "columns";
|
|
542
|
+
|
|
543
|
+
function boardLayoutStorageKey(scope) {
|
|
544
|
+
return `sanity.workflows.tool.board-layout:${scope}`;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
function layoutOf(raw) {
|
|
548
|
+
return BOARD_LAYOUTS.find(layout => layout === raw) ?? BOARD_DEFAULT_LAYOUT;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
function createBoardLayoutStore(args) {
|
|
552
|
+
const storage = args.storage ?? defaultStorage();
|
|
553
|
+
return {
|
|
554
|
+
read: () => layoutOf(readItem(storage, args.storageKey).raw),
|
|
555
|
+
set(layout) {
|
|
556
|
+
return writeItem({
|
|
557
|
+
storage: storage,
|
|
558
|
+
key: args.storageKey,
|
|
559
|
+
value: layout
|
|
560
|
+
}), layout;
|
|
561
|
+
}
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const PICKER_RADIUS = 3;
|
|
566
|
+
|
|
567
|
+
function pickerLabel(face) {
|
|
568
|
+
return face.kind === "named" ? face.title : face.kind === "loading" ? "Loading" : "Select a workflow";
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
function WorkflowPicker({face: face, onSelect: onSelect, options: options}) {
|
|
572
|
+
const empty = options.length === 0, innerRadius = useRadiusToken(PICKER_RADIUS) - 1;
|
|
573
|
+
/* @__PURE__ */
|
|
574
|
+
return jsx(Card, {
|
|
575
|
+
border: !0,
|
|
576
|
+
radius: PICKER_RADIUS,
|
|
577
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
578
|
+
align: "center",
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ jsx(Box, {
|
|
581
|
+
paddingLeft: 2,
|
|
582
|
+
paddingRight: 2,
|
|
583
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
584
|
+
muted: !0,
|
|
585
|
+
size: 1,
|
|
586
|
+
children: "Workflow"
|
|
587
|
+
})
|
|
588
|
+
}),
|
|
589
|
+
/* @__PURE__ */ jsx(MenuButton, {
|
|
590
|
+
button: /* @__PURE__ */ jsx(Button, {
|
|
591
|
+
fontSize: 1,
|
|
592
|
+
gap: 2,
|
|
593
|
+
iconRight: ChevronDownIcon,
|
|
594
|
+
mode: "bleed",
|
|
595
|
+
disabled: empty,
|
|
596
|
+
padding: 2,
|
|
597
|
+
radius: 0,
|
|
598
|
+
style: {
|
|
599
|
+
borderRadius: `0 ${innerRadius}px ${innerRadius}px 0`
|
|
600
|
+
},
|
|
601
|
+
text: pickerLabel(face)
|
|
602
|
+
}),
|
|
603
|
+
id: "workflows-board-picker",
|
|
604
|
+
menu: /* @__PURE__ */ jsx(Menu, {
|
|
605
|
+
children: options.map(option => /* @__PURE__ */ jsx(MenuItem, {
|
|
606
|
+
fontSize: 1,
|
|
607
|
+
onClick: () => onSelect(option.name),
|
|
608
|
+
pressed: face.kind === "named" && option.name === face.name,
|
|
609
|
+
text: option.title
|
|
610
|
+
}, option.name))
|
|
611
|
+
}),
|
|
612
|
+
popover: {
|
|
613
|
+
constrainSize: !0,
|
|
614
|
+
fallbackPlacements: [ "top-start" ],
|
|
615
|
+
placement: "bottom-start",
|
|
616
|
+
portal: !0
|
|
617
|
+
}
|
|
618
|
+
}) ]
|
|
619
|
+
})
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
const LAYOUT_LABELS = {
|
|
624
|
+
columns: {
|
|
625
|
+
icon: InlineIcon,
|
|
626
|
+
label: "Show as boards"
|
|
627
|
+
},
|
|
628
|
+
stack: {
|
|
629
|
+
icon: StackCompactIcon,
|
|
630
|
+
label: "Show as list"
|
|
631
|
+
}
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
function LayoutToggle({layout: layout, onChange: onChange}) {
|
|
635
|
+
/* @__PURE__ */
|
|
636
|
+
return jsx(Flex, {
|
|
637
|
+
align: "center",
|
|
638
|
+
gap: 1,
|
|
639
|
+
children: BOARD_LAYOUTS.map(value => {
|
|
640
|
+
const {icon: icon, label: label} = LAYOUT_LABELS[value];
|
|
641
|
+
/* @__PURE__ */
|
|
642
|
+
return jsx(HoverHint, {
|
|
643
|
+
text: label,
|
|
644
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
645
|
+
"aria-label": label,
|
|
646
|
+
"aria-pressed": value === layout,
|
|
647
|
+
fontSize: 1,
|
|
648
|
+
icon: icon,
|
|
649
|
+
mode: "bleed",
|
|
650
|
+
onClick: () => onChange(value),
|
|
651
|
+
padding: 2,
|
|
652
|
+
selected: value === layout
|
|
653
|
+
})
|
|
654
|
+
}, value);
|
|
655
|
+
})
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function DefinitionLink({name: name, onOpen: onOpen}) {
|
|
660
|
+
const {resolvePathFromState: resolvePathFromState} = useRouter();
|
|
661
|
+
/* @__PURE__ */
|
|
662
|
+
return jsx(Button, {
|
|
663
|
+
as: "a",
|
|
664
|
+
fontSize: 1,
|
|
665
|
+
href: resolvePathFromState({
|
|
666
|
+
definitionName: name
|
|
667
|
+
}),
|
|
668
|
+
mode: "bleed",
|
|
669
|
+
onClick: event => {
|
|
670
|
+
isPlainClick(event) && (event.preventDefault(), onOpen(name));
|
|
671
|
+
},
|
|
672
|
+
padding: 2,
|
|
673
|
+
text: "View definition"
|
|
674
|
+
});
|
|
675
|
+
}
|
|
676
|
+
|
|
677
|
+
function BoardControlsPortal({definitionName: definitionName, layout: layout, onChangeLayout: onChangeLayout, face: face, onOpenDefinition: onOpenDefinition, onSelectWorkflow: onSelectWorkflow, options: options, slot: slot}) {
|
|
678
|
+
return slot ? createPortal(
|
|
679
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
680
|
+
align: "center",
|
|
681
|
+
gap: 2,
|
|
682
|
+
children: [
|
|
683
|
+
/* @__PURE__ */ jsx(WorkflowPicker, {
|
|
684
|
+
face: face,
|
|
685
|
+
onSelect: onSelectWorkflow,
|
|
686
|
+
options: options
|
|
687
|
+
}), definitionName === void 0 ? null : /* @__PURE__ */ jsx(DefinitionLink, {
|
|
688
|
+
name: definitionName,
|
|
689
|
+
onOpen: onOpenDefinition
|
|
690
|
+
}),
|
|
691
|
+
/* @__PURE__ */ jsx(Box, {
|
|
692
|
+
flex: 1
|
|
693
|
+
}),
|
|
694
|
+
/* @__PURE__ */ jsx(LayoutToggle, {
|
|
695
|
+
layout: layout,
|
|
696
|
+
onChange: onChangeLayout
|
|
697
|
+
}) ]
|
|
698
|
+
}), slot) : null;
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
function boardWorkflowOptions(definitions) {
|
|
702
|
+
return definitions.map(definition => ({
|
|
703
|
+
name: definition.name,
|
|
704
|
+
title: definition.title
|
|
705
|
+
})).sort(byTitleThenName);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
function boardLandingName(selection) {
|
|
709
|
+
if (!(selection.kind !== "resolved" || selection.via !== "default")) return selection.definition.name;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
function boardPickerFace(args) {
|
|
713
|
+
const {selection: selection, routeName: routeName} = args;
|
|
714
|
+
if (selection.kind === "resolved") {
|
|
715
|
+
const {name: name, title: title} = selection.definition;
|
|
716
|
+
return {
|
|
717
|
+
kind: "named",
|
|
718
|
+
name: name,
|
|
719
|
+
title: title
|
|
720
|
+
};
|
|
721
|
+
}
|
|
722
|
+
return selection.kind === "loading" && routeName !== void 0 ? {
|
|
723
|
+
kind: "loading"
|
|
724
|
+
} : {
|
|
725
|
+
kind: "none"
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
function boardSelectedPayload(selection) {
|
|
730
|
+
if (selection.kind === "resolved") return {
|
|
731
|
+
via: selection.via,
|
|
732
|
+
...definitionFingerprint(selection.definition)
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
function resolveBoardSelection(args) {
|
|
737
|
+
const {definitions: definitions, routeName: routeName} = args;
|
|
738
|
+
if (definitions === void 0) return {
|
|
739
|
+
kind: "loading"
|
|
740
|
+
};
|
|
741
|
+
if (routeName !== void 0) {
|
|
742
|
+
const named = definitions.find(definition => definition.name === routeName);
|
|
743
|
+
return named ? {
|
|
744
|
+
kind: "resolved",
|
|
745
|
+
definition: named,
|
|
746
|
+
via: "address"
|
|
747
|
+
} : {
|
|
748
|
+
kind: "unknown",
|
|
749
|
+
name: routeName
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
const [landing] = [ ...definitions ].sort(byTitleThenName);
|
|
753
|
+
return landing ? {
|
|
754
|
+
kind: "resolved",
|
|
755
|
+
definition: landing,
|
|
756
|
+
via: "default"
|
|
757
|
+
} : {
|
|
758
|
+
kind: "empty"
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function StageBand({bands: bands, group: group}) {
|
|
763
|
+
const {open: open, toggle: toggle} = useBandOpen(bands, group.stage);
|
|
764
|
+
/* @__PURE__ */
|
|
765
|
+
return jsx(CollapsibleBand, {
|
|
766
|
+
background: !0,
|
|
767
|
+
onToggle: toggle,
|
|
768
|
+
open: open,
|
|
769
|
+
sticky: !0,
|
|
770
|
+
title: group.title,
|
|
771
|
+
header: /* @__PURE__ */ jsx(StageGroupHeading, {
|
|
772
|
+
group: group
|
|
773
|
+
}),
|
|
774
|
+
children: /* @__PURE__ */ jsx(Stack, {
|
|
775
|
+
gap: 1,
|
|
776
|
+
paddingLeft: 5,
|
|
777
|
+
children: group.cards.length === 0 ?
|
|
778
|
+
/* @__PURE__ */ jsx(Box, {
|
|
779
|
+
padding: 3,
|
|
780
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
781
|
+
muted: !0,
|
|
782
|
+
size: 1,
|
|
783
|
+
children: BOARD_EMPTY_STAGE_NOTE
|
|
784
|
+
})
|
|
785
|
+
}) : group.cards.map(card => /* @__PURE__ */ jsx(BoardCard, {
|
|
786
|
+
card: card,
|
|
787
|
+
size: "slim"
|
|
788
|
+
}, card.instanceId))
|
|
789
|
+
})
|
|
790
|
+
});
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function BoardStack({bands: bands, board: board}) {
|
|
794
|
+
/* @__PURE__ */
|
|
795
|
+
return jsx(Stack, {
|
|
796
|
+
gap: 1,
|
|
797
|
+
paddingBottom: 4,
|
|
798
|
+
paddingTop: 3,
|
|
799
|
+
paddingX: TOOL_PANEL_PADDING,
|
|
800
|
+
children: board.groups.map(group => /* @__PURE__ */ jsx(StageBand, {
|
|
801
|
+
bands: bands,
|
|
802
|
+
group: group
|
|
803
|
+
}, group.stage))
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
const ALIAS_PREFIX = "__seed_", MAX_PATH_DEPTH = 3, IDENTIFIER = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
808
|
+
|
|
809
|
+
function previewSelectOf(schemaType) {
|
|
810
|
+
const select = schemaType.preview?.select;
|
|
811
|
+
if (!(select === void 0 || Object.keys(select).length === 0)) return select;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
function fieldTypeOf(parent, name) {
|
|
815
|
+
if (!(!("fields" in parent) || !Array.isArray(parent.fields))) return parent.fields.find(field => field.name === name)?.type;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
function stepOf(args) {
|
|
819
|
+
const {parent: parent, segment: segment, terminal: terminal} = args, fieldType = fieldTypeOf(parent, segment);
|
|
820
|
+
if (fieldType !== void 0) {
|
|
821
|
+
if (terminal) return {
|
|
822
|
+
part: segment,
|
|
823
|
+
next: fieldType
|
|
824
|
+
};
|
|
825
|
+
if (isReferenceSchemaType(fieldType)) {
|
|
826
|
+
const target = fieldType.to.length === 1 ? fieldType.to[0] : void 0;
|
|
827
|
+
return target === void 0 ? void 0 : {
|
|
828
|
+
part: `${segment}->`,
|
|
829
|
+
next: target
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
return {
|
|
833
|
+
part: `${segment}.`,
|
|
834
|
+
next: fieldType
|
|
835
|
+
};
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
function compilePath(root, path) {
|
|
840
|
+
const segments = path.split(".");
|
|
841
|
+
if (segments.length > MAX_PATH_DEPTH || segments.some(s => !IDENTIFIER.test(s))) return;
|
|
842
|
+
let parent = root;
|
|
843
|
+
const parts = [];
|
|
844
|
+
for (const [index, segment] of segments.entries()) {
|
|
845
|
+
const step = stepOf({
|
|
846
|
+
parent: parent,
|
|
847
|
+
segment: segment,
|
|
848
|
+
terminal: index === segments.length - 1
|
|
849
|
+
});
|
|
850
|
+
if (step === void 0) return;
|
|
851
|
+
parts.push(step.part), parent = step.next;
|
|
852
|
+
}
|
|
853
|
+
return parts.join("");
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function selectArmOf(schemaType) {
|
|
857
|
+
const select = previewSelectOf(schemaType);
|
|
858
|
+
if (select === void 0) return;
|
|
859
|
+
const entries = [];
|
|
860
|
+
for (const [key, path] of Object.entries(select)) {
|
|
861
|
+
const compiled = compilePath(schemaType, path);
|
|
862
|
+
if (compiled === void 0) return;
|
|
863
|
+
entries.push(`${JSON.stringify(ALIAS_PREFIX + key)}: ${compiled}`);
|
|
864
|
+
}
|
|
865
|
+
return `_type == ${JSON.stringify(schemaType.name)} => {${entries.join(", ")}}`;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
function planTitleSeed(types) {
|
|
869
|
+
const seedable = /* @__PURE__ */ new Map, arms = [];
|
|
870
|
+
for (const type of types) {
|
|
871
|
+
const arm = selectArmOf(type);
|
|
872
|
+
arm !== void 0 && (seedable.set(type.name, type), arms.push(arm));
|
|
873
|
+
}
|
|
874
|
+
return {
|
|
875
|
+
query: `*[_id in $ids]{_id, _type${arms.length > 0 ? `, ...select(${arms.join(", ")})` : ""}}`,
|
|
876
|
+
seedable: seedable
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
function isSeedRow(value) {
|
|
881
|
+
if (typeof value != "object" || value === null) return !1;
|
|
882
|
+
const row = value;
|
|
883
|
+
return typeof row._id == "string" && typeof row._type == "string";
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
function seededTitleOf(row, schemaType) {
|
|
887
|
+
const select = previewSelectOf(schemaType);
|
|
888
|
+
if (select === void 0) return;
|
|
889
|
+
const selected = {};
|
|
890
|
+
for (const key of Object.keys(select)) selected[key] = row[ALIAS_PREFIX + key];
|
|
891
|
+
const prepare = schemaType.preview?.prepare;
|
|
892
|
+
let title;
|
|
893
|
+
if (prepare === void 0) title = selected.title; else try {
|
|
894
|
+
title = prepare(selected).title;
|
|
895
|
+
} catch (err) {
|
|
896
|
+
console.error(`[workflow-studio-plugin] preview prepare for "${schemaType.name}" failed on seed:`, err);
|
|
897
|
+
return;
|
|
898
|
+
}
|
|
899
|
+
return typeof title == "string" ? title : void 0;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function seedFromRows(args) {
|
|
903
|
+
const rows = args.rows.filter(isSeedRow), byBareId = new Map(rows.map(row => [ row._id, row ])), types = new Map(rows.map(row => [ row._id, row._type ])), titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set;
|
|
904
|
+
for (const {gdrId: gdrId, bareId: bareId} of args.docs) {
|
|
905
|
+
const row = byBareId.get(bareId);
|
|
906
|
+
if (row === void 0) continue;
|
|
907
|
+
const schemaType = args.seedable.get(row._type);
|
|
908
|
+
if (schemaType === void 0) continue;
|
|
909
|
+
resolved.add(gdrId);
|
|
910
|
+
const title = seededTitleOf(row, schemaType);
|
|
911
|
+
title !== void 0 && titles.set(gdrId, title);
|
|
912
|
+
}
|
|
913
|
+
return {
|
|
914
|
+
types: types,
|
|
915
|
+
titles: {
|
|
916
|
+
titles: titles,
|
|
917
|
+
resolved: resolved
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
function titleDrift(seed, live) {
|
|
923
|
+
const drifted = [];
|
|
924
|
+
for (const gdrId of seed.resolved) {
|
|
925
|
+
if (!live.resolved.has(gdrId)) continue;
|
|
926
|
+
const seeded = seed.titles.get(gdrId), observed = live.titles.get(gdrId);
|
|
927
|
+
seeded !== observed && drifted.push({
|
|
928
|
+
gdrId: gdrId,
|
|
929
|
+
seeded: seeded,
|
|
930
|
+
observed: observed
|
|
931
|
+
});
|
|
932
|
+
}
|
|
933
|
+
return drifted;
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function mergeSeededTitles(live, seed) {
|
|
937
|
+
if (seed.resolved.size === 0) return live;
|
|
938
|
+
const titles = new Map(live.titles), resolved = new Set(live.resolved);
|
|
939
|
+
for (const gdrId of seed.resolved) {
|
|
940
|
+
if (live.resolved.has(gdrId)) continue;
|
|
941
|
+
resolved.add(gdrId);
|
|
942
|
+
const title = seed.titles.get(gdrId);
|
|
943
|
+
title !== void 0 && titles.set(gdrId, title);
|
|
944
|
+
}
|
|
945
|
+
return {
|
|
946
|
+
titles: titles,
|
|
947
|
+
resolved: resolved
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
|
|
951
|
+
const NO_TITLES = {
|
|
952
|
+
titles: /* @__PURE__ */ new Map,
|
|
953
|
+
resolved: /* @__PURE__ */ new Set
|
|
954
|
+
};
|
|
955
|
+
|
|
956
|
+
function useDocPreviewTitles(documents) {
|
|
957
|
+
const previewStore = useDocumentPreviewStore(), schema = useSchema(), {perspectiveStack: perspectiveStack} = usePerspective(), {actualTypes: actualTypes, binding: binding} = useWorkflowContext(), [state, setState] = useState(NO_TITLES), key = documents.map(doc => doc.id).join("|");
|
|
958
|
+
return useEffect(() => {
|
|
959
|
+
const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, previews = /* @__PURE__ */ new Map, publish = () => setState({
|
|
960
|
+
titles: new Map(titles),
|
|
961
|
+
resolved: new Set(resolved)
|
|
962
|
+
}), localDocs = documents.flatMap(doc => {
|
|
963
|
+
const {parsed: parsed, local: local} = gdrLocality(doc.id, binding.contentResource);
|
|
964
|
+
return !parsed || !local ? (resolved.add(doc.id), []) : [ {
|
|
965
|
+
doc: doc,
|
|
966
|
+
bareId: parsed.documentId
|
|
967
|
+
} ];
|
|
968
|
+
}), untracks = localDocs.map(({bareId: bareId}) => actualTypes.track(bareId)), dropPreview = docId => {
|
|
969
|
+
previews.get(docId)?.unsubscribe(), previews.delete(docId), titles.delete(docId);
|
|
970
|
+
}, observe = ({docId: docId, bareId: bareId, typeName: typeName}) => {
|
|
971
|
+
const schemaType = openableSchemaType(schema, typeName);
|
|
972
|
+
if (!schemaType) return !1;
|
|
973
|
+
if (previews.get(docId)?.typeName === typeName) return !0;
|
|
974
|
+
dropPreview(docId), resolved.delete(docId);
|
|
975
|
+
const subscription = previewStore.observeForPreview({
|
|
976
|
+
_id: bareId,
|
|
977
|
+
_type: schemaType.name
|
|
978
|
+
}, schemaType, {
|
|
979
|
+
perspective: perspectiveStack
|
|
980
|
+
}).subscribe({
|
|
981
|
+
next: event => {
|
|
982
|
+
resolved.add(docId);
|
|
983
|
+
const title = event.snapshot?.title;
|
|
984
|
+
typeof title == "string" ? titles.set(docId, title) : titles.delete(docId), publish();
|
|
985
|
+
},
|
|
986
|
+
error: err => {
|
|
987
|
+
console.error(`[workflow-studio-plugin] preview for "${docId}" failed:`, err), resolved.add(docId),
|
|
988
|
+
publish();
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
return previews.set(docId, {
|
|
992
|
+
typeName: typeName,
|
|
993
|
+
unsubscribe: () => subscription.unsubscribe()
|
|
994
|
+
}), !0;
|
|
995
|
+
}, evaluate = () => {
|
|
996
|
+
for (const {doc: doc, bareId: bareId} of localDocs) {
|
|
997
|
+
const probe = actualTypes.read(bareId);
|
|
998
|
+
if (probe === null) {
|
|
999
|
+
dropPreview(doc.id), resolved.add(doc.id);
|
|
1000
|
+
continue;
|
|
1001
|
+
}
|
|
1002
|
+
!observe({
|
|
1003
|
+
docId: doc.id,
|
|
1004
|
+
bareId: bareId,
|
|
1005
|
+
typeName: probe ?? doc.type
|
|
1006
|
+
}) && typeof probe == "string" && (dropPreview(doc.id), resolved.add(doc.id));
|
|
1007
|
+
}
|
|
1008
|
+
publish();
|
|
1009
|
+
}, unsubscribeProbes = actualTypes.subscribe(evaluate);
|
|
1010
|
+
return evaluate(), () => {
|
|
1011
|
+
unsubscribeProbes(), untracks.forEach(untrack => untrack()), previews.forEach(preview => preview.unsubscribe());
|
|
1012
|
+
};
|
|
1013
|
+
}, [ key, previewStore, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
|
|
1014
|
+
state;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
function usePreviewSeed(documents) {
|
|
1018
|
+
const client = useClient({
|
|
1019
|
+
apiVersion: WORKFLOW_API_VERSION
|
|
1020
|
+
}), schema = useSchema(), {perspectiveStack: perspectiveStack} = usePerspective(), {actualTypes: actualTypes, binding: binding} = useWorkflowContext(), [seed, setSeed] = useState(NO_TITLES), docs = useMemo(() => documents.flatMap(doc => {
|
|
1021
|
+
const {parsed: parsed, local: local} = gdrLocality(doc.id, binding.contentResource);
|
|
1022
|
+
return parsed === void 0 || !local ? [] : [ {
|
|
1023
|
+
gdrId: doc.id,
|
|
1024
|
+
bareId: parsed.documentId
|
|
1025
|
+
} ];
|
|
1026
|
+
}), [ documents, binding.contentResource ]), key = docs.map(doc => doc.gdrId).join("|");
|
|
1027
|
+
return useEffect(() => {
|
|
1028
|
+
if (setSeed(NO_TITLES), key === "") return;
|
|
1029
|
+
const plan = planTitleSeed(schema.getTypeNames().flatMap(name => openableSchemaType(schema, name) ?? []));
|
|
1030
|
+
let cancelled = !1;
|
|
1031
|
+
return client.fetch(plan.query, {
|
|
1032
|
+
ids: docs.map(doc => doc.bareId)
|
|
1033
|
+
}, {
|
|
1034
|
+
tag: "workflows.preview-seed",
|
|
1035
|
+
...perspectiveStack.length > 0 ? {
|
|
1036
|
+
perspective: [ ...perspectiveStack ]
|
|
1037
|
+
} : {}
|
|
1038
|
+
}).then(rows => {
|
|
1039
|
+
if (cancelled) return;
|
|
1040
|
+
const result = seedFromRows({
|
|
1041
|
+
rows: rows,
|
|
1042
|
+
docs: docs,
|
|
1043
|
+
seedable: plan.seedable
|
|
1044
|
+
});
|
|
1045
|
+
actualTypes.seed(result.types), setSeed(result.titles);
|
|
1046
|
+
}).catch(err => {
|
|
1047
|
+
cancelled || console.error("[workflow-studio-plugin] preview seed failed:", err);
|
|
1048
|
+
}), () => {
|
|
1049
|
+
cancelled = !0;
|
|
1050
|
+
};
|
|
1051
|
+
}, [ key, client, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
|
|
1052
|
+
seed;
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function useSeededTitles(args) {
|
|
1056
|
+
const {ordering: ordering, warm: warm} = args, seed = usePreviewSeed(warm), live = useDocPreviewTitles(ordering), telemetry = useWorkflowTelemetry(), warned = useRef(/* @__PURE__ */ new Set);
|
|
1057
|
+
return useEffect(() => {
|
|
1058
|
+
const fresh = titleDrift(seed, live).filter(drift => !warned.current.has(drift.gdrId));
|
|
1059
|
+
if (fresh.length !== 0) {
|
|
1060
|
+
warned.current.size === 0 && telemetry.log(WorkflowTitleSeedDrifted, {
|
|
1061
|
+
driftCount: fresh.length
|
|
1062
|
+
});
|
|
1063
|
+
for (const drift of fresh) warned.current.add(drift.gdrId), console.warn(`[workflow-studio-plugin] seeded preview title for "${drift.gdrId}" diverged from the live pipeline — the seed projection or prepare mirroring is drifting from Studio preview resolution`);
|
|
1064
|
+
}
|
|
1065
|
+
}, [ seed, live, telemetry ]), useMemo(() => {
|
|
1066
|
+
const merged = mergeSeededTitles(live, seed);
|
|
1067
|
+
return {
|
|
1068
|
+
...merged,
|
|
1069
|
+
ready: ordering.every(doc => merged.resolved.has(doc.id))
|
|
1070
|
+
};
|
|
1071
|
+
}, [ ordering, live, seed ]);
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
function useGroupTitles(groups) {
|
|
1075
|
+
const ordering = useMemo(() => groups.withDocuments.map(group => group.document), [ groups ]), warm = useMemo(() => groups.withDocuments.flatMap(group => group.documents), [ groups ]);
|
|
1076
|
+
return useSeededTitles({
|
|
1077
|
+
ordering: ordering,
|
|
1078
|
+
warm: warm
|
|
1079
|
+
});
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
const NO_DOCUMENTS = [];
|
|
1083
|
+
|
|
1084
|
+
function useBoard(args) {
|
|
1085
|
+
const {definition: definition, instances: instances} = args, derived = useMemo(() => definition ? deriveBoard({
|
|
1086
|
+
instances: instances,
|
|
1087
|
+
definition: definition
|
|
1088
|
+
}) : void 0, [ definition, instances ]), documents = useMemo(() => derived ? boardDocuments(derived) : NO_DOCUMENTS, [ derived ]), {titles: titles, ready: ready} = useSeededTitles({
|
|
1089
|
+
ordering: documents,
|
|
1090
|
+
warm: documents
|
|
1091
|
+
}), board = useMemo(() => derived ? orderBoardByPreviewTitle(derived, titles) : void 0, [ derived, titles ]);
|
|
1092
|
+
return useMemo(() => ({
|
|
1093
|
+
board: board,
|
|
1094
|
+
ready: ready
|
|
1095
|
+
}), [ board, ready ]);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
const INSTANCE_CAP = 200;
|
|
1099
|
+
|
|
1100
|
+
function useCappedInstances(filter) {
|
|
1101
|
+
const {engine: engine} = useWorkflowContext(), capped = useMemo(() => ({
|
|
1102
|
+
...filter,
|
|
1103
|
+
limit: INSTANCE_CAP
|
|
1104
|
+
}), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable} = useWorkflowInstances({
|
|
1105
|
+
engine: engine,
|
|
1106
|
+
filter: capped
|
|
1107
|
+
});
|
|
1108
|
+
return useMemo(() => {
|
|
1109
|
+
const rows = instances ?? [];
|
|
1110
|
+
return {
|
|
1111
|
+
rows: rows,
|
|
1112
|
+
truncated: rows.length + unreadable.length === INSTANCE_CAP,
|
|
1113
|
+
loading: loading,
|
|
1114
|
+
unreadable: unreadable
|
|
1115
|
+
};
|
|
1116
|
+
}, [ instances, loading, unreadable ]);
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
const BOARD_CAP_NOTE = `Showing the latest ${INSTANCE_CAP} workflows — older ones aren’t on the board.`, NOTHING = {
|
|
1120
|
+
ids: []
|
|
1121
|
+
};
|
|
1122
|
+
|
|
1123
|
+
function useBoardInstances(definitionName) {
|
|
1124
|
+
const filter = useMemo(() => definitionName === void 0 ? NOTHING : {
|
|
1125
|
+
definition: definitionName,
|
|
1126
|
+
includeCompleted: !0
|
|
1127
|
+
}, [ definitionName ]);
|
|
1128
|
+
return useCappedInstances(filter);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
function useBoardLayout(scope) {
|
|
1132
|
+
const store = useMemo(() => createBoardLayoutStore({
|
|
1133
|
+
storageKey: boardLayoutStorageKey(scope)
|
|
1134
|
+
}), [ scope ]), [held, setHeld] = useState(() => ({
|
|
1135
|
+
store: store,
|
|
1136
|
+
layout: store.read()
|
|
1137
|
+
}));
|
|
1138
|
+
return held.store !== store && setHeld({
|
|
1139
|
+
store: store,
|
|
1140
|
+
layout: store.read()
|
|
1141
|
+
}), {
|
|
1142
|
+
layout: held.layout,
|
|
1143
|
+
setLayout: layout => setHeld({
|
|
1144
|
+
store: store,
|
|
1145
|
+
layout: store.set(layout)
|
|
1146
|
+
})
|
|
1147
|
+
};
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
function DocumentsTab({filterSlot: filterSlot, onOpenDefinition: onOpenDefinition, onSelectWorkflow: onSelectWorkflow, routeDefinition: routeDefinition}) {
|
|
1151
|
+
const {binding: binding} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), {definitions: definitions, error: error} = useDeployedDefinitions(), {layout: layout, setLayout: setLayout} = useBoardLayout(binding.engineResource.id), bands = useBandOpenState({
|
|
1152
|
+
segment: "documents",
|
|
1153
|
+
scope: binding.engineResource.id
|
|
1154
|
+
}), selection = resolveBoardSelection({
|
|
1155
|
+
definitions: definitions,
|
|
1156
|
+
routeName: routeDefinition
|
|
1157
|
+
}), selected = selection.kind === "resolved" ? selection.definition : void 0, instances = useBoardInstances(selected?.name), {board: board, ready: ready} = useBoard({
|
|
1158
|
+
definition: selected,
|
|
1159
|
+
instances: instances.rows
|
|
1160
|
+
}), revealed = useRevealGate({
|
|
1161
|
+
loading: instances.loading,
|
|
1162
|
+
ready: ready
|
|
1163
|
+
}), landing = boardLandingName(selection);
|
|
1164
|
+
useEffect(() => {
|
|
1165
|
+
landing !== void 0 && onSelectWorkflow(landing, {
|
|
1166
|
+
replace: !0
|
|
1167
|
+
});
|
|
1168
|
+
}, [ landing, onSelectWorkflow ]);
|
|
1169
|
+
const selectedPayload = boardSelectedPayload(selection);
|
|
1170
|
+
/* @__PURE__ */
|
|
1171
|
+
return jsxs(Flex, {
|
|
1172
|
+
direction: "column",
|
|
1173
|
+
flex: 1,
|
|
1174
|
+
style: {
|
|
1175
|
+
minHeight: 0
|
|
1176
|
+
},
|
|
1177
|
+
children: [
|
|
1178
|
+
/* @__PURE__ */ jsx(BoardControlsPortal, {
|
|
1179
|
+
definitionName: selected?.name,
|
|
1180
|
+
layout: layout,
|
|
1181
|
+
onOpenDefinition: onOpenDefinition,
|
|
1182
|
+
onChangeLayout: next => {
|
|
1183
|
+
next !== layout && (telemetry.log(WorkflowBoardLayoutChanged, {
|
|
1184
|
+
layout: next
|
|
1185
|
+
}), setLayout(next));
|
|
1186
|
+
},
|
|
1187
|
+
onSelectWorkflow: onSelectWorkflow,
|
|
1188
|
+
options: boardWorkflowOptions(definitions ?? []),
|
|
1189
|
+
face: boardPickerFace({
|
|
1190
|
+
selection: selection,
|
|
1191
|
+
routeName: routeDefinition
|
|
1192
|
+
}),
|
|
1193
|
+
slot: filterSlot
|
|
1194
|
+
}),
|
|
1195
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
1196
|
+
direction: "column",
|
|
1197
|
+
flex: 1,
|
|
1198
|
+
gap: 2,
|
|
1199
|
+
style: {
|
|
1200
|
+
minHeight: 0
|
|
1201
|
+
},
|
|
1202
|
+
children: [ instances.unreadable.length === 0 ? null : /* @__PURE__ */ jsx(Box, {
|
|
1203
|
+
paddingX: TOOL_PANEL_PADDING,
|
|
1204
|
+
children: /* @__PURE__ */ jsx(UnreadableDocsNote, {
|
|
1205
|
+
unreadable: instances.unreadable
|
|
1206
|
+
})
|
|
1207
|
+
}),
|
|
1208
|
+
/* @__PURE__ */ jsx(BoardBody, {
|
|
1209
|
+
bands: bands,
|
|
1210
|
+
board: board,
|
|
1211
|
+
error: error,
|
|
1212
|
+
layout: layout,
|
|
1213
|
+
loading: !revealed,
|
|
1214
|
+
selection: selection
|
|
1215
|
+
}), instances.truncated ? /* @__PURE__ */ jsx(Box, {
|
|
1216
|
+
paddingX: TOOL_PANEL_PADDING,
|
|
1217
|
+
children: /* @__PURE__ */ jsx(MutedNote, {
|
|
1218
|
+
text: BOARD_CAP_NOTE
|
|
1219
|
+
})
|
|
1220
|
+
}) : null ]
|
|
1221
|
+
}), selectedPayload === void 0 ? null :
|
|
1222
|
+
/* @__PURE__ */ jsx(LogEventOnMount, {
|
|
1223
|
+
data: selectedPayload,
|
|
1224
|
+
event: WorkflowBoardWorkflowSelected
|
|
1225
|
+
}, selected?.name) ]
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
function BoardBody({bands: bands, board: board, error: error, layout: layout, loading: loading, selection: selection}) {
|
|
1230
|
+
return error !== void 0 ? /* @__PURE__ */ jsx(StatusBox, {
|
|
1231
|
+
children: /* @__PURE__ */ jsx(MutedNote, {
|
|
1232
|
+
text: `Could not load the deployed workflows: ${error}`
|
|
1233
|
+
})
|
|
1234
|
+
}) : selection.kind === "loading" ? /* @__PURE__ */ jsx(StatusBox, {
|
|
1235
|
+
children: /* @__PURE__ */ jsx(TabStatusRow, {
|
|
1236
|
+
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
1237
|
+
label: "Loading workflows…"
|
|
1238
|
+
})
|
|
1239
|
+
})
|
|
1240
|
+
}) : selection.kind === "empty" ? /* @__PURE__ */ jsx(StatusBox, {
|
|
1241
|
+
children: /* @__PURE__ */ jsx(EmptyState, {
|
|
1242
|
+
description: "Deploy a workflow to see its documents here",
|
|
1243
|
+
icon: /* @__PURE__ */ jsx(SearchIcon, {}),
|
|
1244
|
+
title: "No workflows deployed"
|
|
1245
|
+
})
|
|
1246
|
+
}) : selection.kind === "unknown" ? /* @__PURE__ */ jsx(StatusBox, {
|
|
1247
|
+
children: /* @__PURE__ */ jsx(EmptyState, {
|
|
1248
|
+
description: `No deployed workflow is named “${selection.name}” — pick one above`,
|
|
1249
|
+
icon: /* @__PURE__ */ jsx(SearchIcon, {}),
|
|
1250
|
+
title: "Workflow not found"
|
|
1251
|
+
})
|
|
1252
|
+
}) : loading || board === void 0 ? /* @__PURE__ */ jsx(StatusBox, {
|
|
1253
|
+
children: /* @__PURE__ */ jsx(TabStatusRow, {
|
|
1254
|
+
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
1255
|
+
label: "Loading documents…"
|
|
1256
|
+
})
|
|
1257
|
+
})
|
|
1258
|
+
}) : layout === "stack" ? /* @__PURE__ */ jsx(BoardStack, {
|
|
1259
|
+
bands: bands,
|
|
1260
|
+
board: board
|
|
1261
|
+
}) : /* @__PURE__ */ jsx(StageGroups, {
|
|
1262
|
+
board: board
|
|
1263
|
+
});
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1266
|
+
function StatusBox({children: children}) {
|
|
1267
|
+
/* @__PURE__ */
|
|
1268
|
+
return jsx(Flex, {
|
|
1269
|
+
direction: "column",
|
|
1270
|
+
flex: 1,
|
|
1271
|
+
paddingX: TOOL_PANEL_PADDING,
|
|
1272
|
+
children: children
|
|
1273
|
+
});
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
function definitionCatalog(args) {
|
|
1277
|
+
const rejected = [ ...args.issues.values() ];
|
|
1278
|
+
return args.definitions.map(definition => ({
|
|
1279
|
+
name: definition.name,
|
|
1280
|
+
title: definition.title ?? definition.name,
|
|
1281
|
+
description: definition.description,
|
|
1282
|
+
activeCount: args.inFlight.filter(instance => instance.definition === definition.name).length,
|
|
1283
|
+
docTypes: [ ...new Set(args.mappings.filter(mapping => mapping.definition === definition.name).map(mapping => mapping.docType)) ],
|
|
1284
|
+
brokenBindings: rejected.filter(issue => issue.definition === definition.name && issue.version === definition.version).map(issue => ({
|
|
1285
|
+
docType: issue.docType,
|
|
1286
|
+
detail: mappingIssueDetail(issue)
|
|
1287
|
+
})).sort((a, b) => a.docType.localeCompare(b.docType))
|
|
1288
|
+
})).sort(byTitleThenName);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function undeployedWorkflowNotices(args) {
|
|
1292
|
+
return [ ...args.issues.values() ].filter(namesNoDeployedDefinition).sort((a, b) => a.definition.localeCompare(b.definition) || a.docType.localeCompare(b.docType)).map(issue => `“${issue.definition}” is set up to run on ${args.titleOf(issue.docType) ?? issue.docType} documents, but no deployed workflow has that name`);
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
function docTypeLabels(docTypes, titleOf) {
|
|
1296
|
+
return docTypes.map(name => ({
|
|
1297
|
+
name: name,
|
|
1298
|
+
title: titleOf(name) ?? name
|
|
1299
|
+
})).sort(byTitleThenName);
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
function startPhrase(definition) {
|
|
1303
|
+
if (definition.lifecycle === "child") return "Started by a parent workflow";
|
|
1304
|
+
if (startKindOf(definition) === "autonomous") return "Starts automatically";
|
|
1305
|
+
const subject = (definition.fields ?? []).find(isSubjectEntry);
|
|
1306
|
+
return subject !== void 0 && isInputSourced(subject) ? "Started manually, from a document" : "Started manually";
|
|
1307
|
+
}
|
|
1308
|
+
|
|
1309
|
+
function deployedAtOf(definition) {
|
|
1310
|
+
return typeof definition._updatedAt == "string" ? definition._updatedAt : void 0;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
function deployedPhrase(definition) {
|
|
1314
|
+
const deployedAt = deployedAtOf(definition);
|
|
1315
|
+
if (deployedAt === void 0) return `v${definition.version}`;
|
|
1316
|
+
const ago = formatTimeAgo(deployedAt);
|
|
1317
|
+
return ago === "" ? formatShortDateTime(deployedAt) : `${formatShortDateTime(deployedAt)} (${ago})`;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
function DetailNotFound({back: back, message: message}) {
|
|
1321
|
+
/* @__PURE__ */
|
|
1322
|
+
return jsxs(Stack, {
|
|
1323
|
+
gap: 4,
|
|
1324
|
+
children: [
|
|
1325
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1326
|
+
muted: !0,
|
|
1327
|
+
size: 1,
|
|
1328
|
+
children: message
|
|
1329
|
+
}),
|
|
1330
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
1331
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
1332
|
+
fontSize: 1,
|
|
1333
|
+
gap: 2,
|
|
1334
|
+
icon: ArrowLeftIcon,
|
|
1335
|
+
mode: "ghost",
|
|
1336
|
+
onClick: back.go,
|
|
1337
|
+
padding: 2,
|
|
1338
|
+
text: back.label
|
|
1339
|
+
})
|
|
1340
|
+
}) ]
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
function DetailTitle({busy: busy = !1, documentGdr: documentGdr, title: title}) {
|
|
1345
|
+
const chipPullback = -useSpaceToken(1);
|
|
1346
|
+
/* @__PURE__ */
|
|
1347
|
+
return jsxs(Flex, {
|
|
1348
|
+
align: "center",
|
|
1349
|
+
gap: 2,
|
|
1350
|
+
paddingLeft: 2,
|
|
1351
|
+
children: [
|
|
1352
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1353
|
+
style: {
|
|
1354
|
+
minWidth: 0
|
|
1355
|
+
},
|
|
1356
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1357
|
+
size: 1,
|
|
1358
|
+
textOverflow: "ellipsis",
|
|
1359
|
+
weight: "semibold",
|
|
1360
|
+
children: title
|
|
1361
|
+
})
|
|
1362
|
+
}), documentGdr === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
|
|
1363
|
+
children: [
|
|
1364
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1365
|
+
muted: !0,
|
|
1366
|
+
size: 1,
|
|
1367
|
+
children: "in"
|
|
1368
|
+
}),
|
|
1369
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
1370
|
+
align: "center",
|
|
1371
|
+
flex: "none",
|
|
1372
|
+
style: {
|
|
1373
|
+
height: 0,
|
|
1374
|
+
marginLeft: chipPullback
|
|
1375
|
+
},
|
|
1376
|
+
children: /* @__PURE__ */ jsx(DocPreviewLink, {
|
|
1377
|
+
gdr: documentGdr,
|
|
1378
|
+
layout: "inline",
|
|
1379
|
+
source: "detail-title"
|
|
1380
|
+
})
|
|
1381
|
+
}) ]
|
|
1382
|
+
}),
|
|
1383
|
+
/* @__PURE__ */ jsx(SpinnerSlot, {
|
|
1384
|
+
busy: busy
|
|
1385
|
+
}) ]
|
|
1386
|
+
});
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
const INSTANCE_CAP_COUNT_NOTE = `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren’t counted.`, NEWEST_INSTANCES = {
|
|
1390
|
+
includeCompleted: !0
|
|
1391
|
+
};
|
|
1392
|
+
|
|
1393
|
+
function useToolInstances() {
|
|
1394
|
+
const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable} = useCappedInstances(NEWEST_INSTANCES);
|
|
1395
|
+
return useMemo(() => ({
|
|
1396
|
+
inFlight: rows.filter(instance => terminalState(instance) === "in-flight"),
|
|
1397
|
+
settled: rows.filter(instance => terminalState(instance) !== "in-flight"),
|
|
1398
|
+
truncated: truncated,
|
|
1399
|
+
loading: loading,
|
|
1400
|
+
unreadable: unreadable
|
|
1401
|
+
}), [ rows, truncated, loading, unreadable ]);
|
|
1402
|
+
}
|
|
1403
|
+
|
|
1404
|
+
function DefinitionDetail({definitionName: definitionName, backToList: backToList}) {
|
|
1405
|
+
const {definitions: definitions, error: error} = useDeployedDefinitions(), definition = definitions?.find(row => row.name === definitionName), loading = definitions === void 0 && error === void 0, viewed = loading || error !== void 0 ? null : /* @__PURE__ */ jsx(LogEventOnMount, {
|
|
1406
|
+
data: {
|
|
1407
|
+
found: definition !== void 0,
|
|
1408
|
+
...definitionFingerprint(definition)
|
|
1409
|
+
},
|
|
1410
|
+
event: WorkflowDefinitionDetailViewed
|
|
1411
|
+
});
|
|
1412
|
+
return !loading && error === void 0 && definition === void 0 ? /* @__PURE__ */ jsxs(Box, {
|
|
1413
|
+
paddingX: 3,
|
|
1414
|
+
children: [ viewed,
|
|
1415
|
+
/* @__PURE__ */ jsx(TabStatusRow, {
|
|
1416
|
+
children: /* @__PURE__ */ jsx(DetailNotFound, {
|
|
1417
|
+
back: backToList,
|
|
1418
|
+
message: /* @__PURE__ */ jsxs(Fragment, {
|
|
1419
|
+
children: [ "Unable to find a definition with name “",
|
|
1420
|
+
/* @__PURE__ */ jsx("strong", {
|
|
1421
|
+
children: definitionName
|
|
1422
|
+
}), "”." ]
|
|
1423
|
+
})
|
|
1424
|
+
})
|
|
1425
|
+
}) ]
|
|
1426
|
+
}) : /* @__PURE__ */ jsxs(Flex, {
|
|
1427
|
+
direction: "column",
|
|
1428
|
+
height: "fill",
|
|
1429
|
+
overflow: "hidden",
|
|
1430
|
+
children: [ viewed,
|
|
1431
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1432
|
+
flex: "none",
|
|
1433
|
+
paddingBottom: 2,
|
|
1434
|
+
paddingTop: 3,
|
|
1435
|
+
paddingX: 3,
|
|
1436
|
+
children: loading ? /* @__PURE__ */ jsx(Box, {
|
|
1437
|
+
paddingLeft: 2,
|
|
1438
|
+
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
1439
|
+
label: "Loading definition…"
|
|
1440
|
+
})
|
|
1441
|
+
}) : /* @__PURE__ */ jsx(DetailTitle, {
|
|
1442
|
+
title: definition?.title ?? definitionName
|
|
1443
|
+
})
|
|
1444
|
+
}),
|
|
1445
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1446
|
+
flex: 1,
|
|
1447
|
+
overflow: "auto",
|
|
1448
|
+
paddingX: 3,
|
|
1449
|
+
children: /* @__PURE__ */ jsx(Flex, {
|
|
1450
|
+
direction: "column",
|
|
1451
|
+
paddingBottom: 5,
|
|
1452
|
+
paddingLeft: 2,
|
|
1453
|
+
paddingTop: 4,
|
|
1454
|
+
style: {
|
|
1455
|
+
boxSizing: "border-box",
|
|
1456
|
+
minHeight: "100%"
|
|
1457
|
+
},
|
|
1458
|
+
children: /* @__PURE__ */ jsx(DefinitionBody, {
|
|
1459
|
+
definition: definition,
|
|
1460
|
+
error: error
|
|
1461
|
+
})
|
|
1462
|
+
})
|
|
1463
|
+
}) ]
|
|
1464
|
+
});
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
function DefinitionBody({definition: definition, error: error}) {
|
|
1468
|
+
return error !== void 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1469
|
+
text: `Could not load the deployed workflows: ${error}`
|
|
1470
|
+
}) : definition === void 0 ? null : /* @__PURE__ */ jsx(DefinitionFacts, {
|
|
1471
|
+
definition: definition
|
|
1472
|
+
});
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
function SetupIssues({bindings: bindings}) {
|
|
1476
|
+
const schema = useSchema();
|
|
1477
|
+
return bindings.length === 0 ? null : /* @__PURE__ */ jsx(Card, {
|
|
1478
|
+
padding: 3,
|
|
1479
|
+
radius: 3,
|
|
1480
|
+
tone: "caution",
|
|
1481
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1482
|
+
align: "flex-start",
|
|
1483
|
+
gap: 3,
|
|
1484
|
+
children: [
|
|
1485
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1486
|
+
size: 1,
|
|
1487
|
+
children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
|
|
1488
|
+
}),
|
|
1489
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
1490
|
+
flex: 1,
|
|
1491
|
+
gap: 3,
|
|
1492
|
+
children: [
|
|
1493
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1494
|
+
size: 1,
|
|
1495
|
+
weight: "semibold",
|
|
1496
|
+
children: "Setup issue"
|
|
1497
|
+
}), bindings.map(binding => /* @__PURE__ */ jsx(Text, {
|
|
1498
|
+
size: 1,
|
|
1499
|
+
children: `${schema.get(binding.docType)?.title ?? binding.docType}: ${binding.detail}.`
|
|
1500
|
+
}, binding.docType)),
|
|
1501
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1502
|
+
muted: !0,
|
|
1503
|
+
size: 1,
|
|
1504
|
+
children: "Editors see this workflow disabled on these document types until a developer fixes it."
|
|
1505
|
+
}) ]
|
|
1506
|
+
}) ]
|
|
1507
|
+
})
|
|
1508
|
+
});
|
|
1509
|
+
}
|
|
1510
|
+
|
|
1511
|
+
function DefinitionFacts({definition: definition}) {
|
|
1512
|
+
const {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), snapshotWidth = useContainerToken(2), [row] = definitionCatalog({
|
|
1513
|
+
definitions: [ definition ],
|
|
1514
|
+
mappings: mappings,
|
|
1515
|
+
inFlight: instances.inFlight,
|
|
1516
|
+
issues: mappingIssues
|
|
1517
|
+
}), runsOn = docTypeLabels(row?.docTypes ?? [], name => schema.get(name)?.title).map(docType => docType.title);
|
|
1518
|
+
/* @__PURE__ */
|
|
1519
|
+
return jsxs(Flex, {
|
|
1520
|
+
direction: "column",
|
|
1521
|
+
flex: 1,
|
|
1522
|
+
gap: 4,
|
|
1523
|
+
children: [ row === void 0 || row.brokenBindings.length === 0 ? null : /* @__PURE__ */ jsx(Box, {
|
|
1524
|
+
flex: "none",
|
|
1525
|
+
children: /* @__PURE__ */ jsx(SetupIssues, {
|
|
1526
|
+
bindings: row.brokenBindings
|
|
1527
|
+
})
|
|
1528
|
+
}),
|
|
1529
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1530
|
+
flex: "none",
|
|
1531
|
+
style: {
|
|
1532
|
+
maxWidth: snapshotWidth
|
|
1533
|
+
},
|
|
1534
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1535
|
+
gap: 4,
|
|
1536
|
+
children: [
|
|
1537
|
+
/* @__PURE__ */ jsx(MetaRow, {
|
|
1538
|
+
label: "Deployed",
|
|
1539
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1540
|
+
size: 1,
|
|
1541
|
+
children: deployedPhrase(definition)
|
|
1542
|
+
})
|
|
1543
|
+
}),
|
|
1544
|
+
/* @__PURE__ */ jsx(MetaRow, {
|
|
1545
|
+
label: "Active instances",
|
|
1546
|
+
children: instances.loading ? /* @__PURE__ */ jsx(Text, {
|
|
1547
|
+
muted: !0,
|
|
1548
|
+
size: 1,
|
|
1549
|
+
children: "—"
|
|
1550
|
+
}) : /* @__PURE__ */ jsxs(Stack, {
|
|
1551
|
+
gap: 2,
|
|
1552
|
+
children: [
|
|
1553
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1554
|
+
size: 1,
|
|
1555
|
+
children: row?.activeCount ?? 0
|
|
1556
|
+
}), instances.truncated ? /* @__PURE__ */ jsx(Text, {
|
|
1557
|
+
muted: !0,
|
|
1558
|
+
size: 1,
|
|
1559
|
+
children: INSTANCE_CAP_COUNT_NOTE
|
|
1560
|
+
}) : null ]
|
|
1561
|
+
})
|
|
1562
|
+
}),
|
|
1563
|
+
/* @__PURE__ */ jsx(MetaRow, {
|
|
1564
|
+
label: "Trigger",
|
|
1565
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1566
|
+
size: 1,
|
|
1567
|
+
children: startPhrase(definition)
|
|
1568
|
+
})
|
|
1569
|
+
}),
|
|
1570
|
+
/* @__PURE__ */ jsx(MetaRow, {
|
|
1571
|
+
label: "Document types",
|
|
1572
|
+
children: runsOn.length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
1573
|
+
muted: !0,
|
|
1574
|
+
size: 1,
|
|
1575
|
+
children: "No document types in this Studio"
|
|
1576
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
1577
|
+
size: 1,
|
|
1578
|
+
children: runsOn.join(", ")
|
|
1579
|
+
})
|
|
1580
|
+
}),
|
|
1581
|
+
/* @__PURE__ */ jsx(MetaRow, {
|
|
1582
|
+
label: "Description",
|
|
1583
|
+
children: definition.description === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
1584
|
+
muted: !0,
|
|
1585
|
+
size: 1,
|
|
1586
|
+
children: /* @__PURE__ */ jsx("em", {
|
|
1587
|
+
children: "No description"
|
|
1588
|
+
})
|
|
1589
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
1590
|
+
size: 1,
|
|
1591
|
+
style: {
|
|
1592
|
+
whiteSpace: "pre-wrap"
|
|
1593
|
+
},
|
|
1594
|
+
children: definition.description
|
|
1595
|
+
})
|
|
1596
|
+
}) ]
|
|
1597
|
+
})
|
|
1598
|
+
}),
|
|
1599
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
1600
|
+
direction: "column",
|
|
1601
|
+
flex: 1,
|
|
1602
|
+
style: {
|
|
1603
|
+
minHeight: 340
|
|
1604
|
+
},
|
|
1605
|
+
children: /* @__PURE__ */ jsx(MetaRow, {
|
|
1606
|
+
fillHeight: !0,
|
|
1607
|
+
label: "Stages",
|
|
1608
|
+
children: /* @__PURE__ */ jsx(WorkflowDiagram, {
|
|
1609
|
+
definition: definition,
|
|
1610
|
+
explain: !0,
|
|
1611
|
+
fill: !0,
|
|
1612
|
+
height: "100%"
|
|
1613
|
+
})
|
|
1614
|
+
})
|
|
1615
|
+
}) ]
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
const MAX_VISIBLE_DOC_TYPES = 4, COLUMNS = {
|
|
1620
|
+
definition: 6,
|
|
1621
|
+
instances: 2,
|
|
1622
|
+
docTypes: 2
|
|
1623
|
+
};
|
|
1624
|
+
|
|
1625
|
+
function Col({flex: flex, children: children}) {
|
|
1626
|
+
/* @__PURE__ */
|
|
1627
|
+
return jsx(Box, {
|
|
1628
|
+
flex: flex,
|
|
1629
|
+
paddingRight: 3,
|
|
1630
|
+
children: children
|
|
1631
|
+
});
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
function DefinitionsTab({onOpenDefinition: onOpenDefinition}) {
|
|
1635
|
+
const {definitions: definitions, error: error} = useDeployedDefinitions(), {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), undeployed = /* @__PURE__ */ jsx(UndeployedWorkflows, {
|
|
1636
|
+
notices: undeployedWorkflowNotices({
|
|
1637
|
+
issues: mappingIssues,
|
|
1638
|
+
titleOf: name => schema.get(name)?.title
|
|
1639
|
+
})
|
|
1640
|
+
});
|
|
1641
|
+
if (error !== void 0) /* @__PURE__ */
|
|
1642
|
+
return jsx(TabStatusRow, {
|
|
1643
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1644
|
+
muted: !0,
|
|
1645
|
+
size: 1,
|
|
1646
|
+
children: `Could not load the deployed workflows: ${error}`
|
|
1647
|
+
})
|
|
1648
|
+
});
|
|
1649
|
+
if (definitions === void 0) /* @__PURE__ */
|
|
1650
|
+
return jsx(TabStatusRow, {
|
|
1651
|
+
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
1652
|
+
label: "Loading definitions…"
|
|
1653
|
+
})
|
|
1654
|
+
});
|
|
1655
|
+
if (definitions.length === 0) /* @__PURE__ */
|
|
1656
|
+
return jsxs(Flex, {
|
|
1657
|
+
direction: "column",
|
|
1658
|
+
flex: 1,
|
|
1659
|
+
gap: 3,
|
|
1660
|
+
paddingBottom: 4,
|
|
1661
|
+
paddingTop: 3,
|
|
1662
|
+
children: [ undeployed,
|
|
1663
|
+
/* @__PURE__ */ jsx(EmptyState, {
|
|
1664
|
+
description: "Workflow definitions deployed to this project will appear here",
|
|
1665
|
+
icon: /* @__PURE__ */ jsx(TransferIcon, {}),
|
|
1666
|
+
title: "No definitions deployed"
|
|
1667
|
+
}) ]
|
|
1668
|
+
});
|
|
1669
|
+
const rows = definitionCatalog({
|
|
1670
|
+
definitions: definitions,
|
|
1671
|
+
mappings: mappings,
|
|
1672
|
+
inFlight: instances.inFlight,
|
|
1673
|
+
issues: mappingIssues
|
|
1674
|
+
}), active = rows.reduce((sum, row) => sum + row.activeCount, 0);
|
|
1675
|
+
/* @__PURE__ */
|
|
1676
|
+
return jsxs(Stack, {
|
|
1677
|
+
gap: 3,
|
|
1678
|
+
paddingBottom: 4,
|
|
1679
|
+
paddingTop: 3,
|
|
1680
|
+
children: [ undeployed,
|
|
1681
|
+
/* @__PURE__ */ jsx(Box, {
|
|
1682
|
+
paddingX: 2,
|
|
1683
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1684
|
+
align: "center",
|
|
1685
|
+
gap: 2,
|
|
1686
|
+
children: [
|
|
1687
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1688
|
+
size: 1,
|
|
1689
|
+
weight: "semibold",
|
|
1690
|
+
children: pluralize("definition", rows.length, !0)
|
|
1691
|
+
}),
|
|
1692
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1693
|
+
muted: !0,
|
|
1694
|
+
size: 1,
|
|
1695
|
+
children: instances.loading ? "counting active instances…" : activeSummary(active)
|
|
1696
|
+
}) ]
|
|
1697
|
+
})
|
|
1698
|
+
}),
|
|
1699
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
1700
|
+
gap: 2,
|
|
1701
|
+
children: [
|
|
1702
|
+
/* @__PURE__ */ jsx(Card, {
|
|
1703
|
+
paddingX: 2,
|
|
1704
|
+
paddingY: 2,
|
|
1705
|
+
style: {
|
|
1706
|
+
position: "sticky",
|
|
1707
|
+
top: 0,
|
|
1708
|
+
zIndex: 1
|
|
1709
|
+
},
|
|
1710
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1711
|
+
align: "center",
|
|
1712
|
+
gap: 4,
|
|
1713
|
+
children: [
|
|
1714
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1715
|
+
flex: COLUMNS.definition,
|
|
1716
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1717
|
+
muted: !0,
|
|
1718
|
+
size: 1,
|
|
1719
|
+
weight: "medium",
|
|
1720
|
+
children: "Definition"
|
|
1721
|
+
})
|
|
1722
|
+
}),
|
|
1723
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1724
|
+
flex: COLUMNS.instances,
|
|
1725
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1726
|
+
align: "center",
|
|
1727
|
+
gap: 2,
|
|
1728
|
+
children: [
|
|
1729
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1730
|
+
muted: !0,
|
|
1731
|
+
size: 1,
|
|
1732
|
+
weight: "medium",
|
|
1733
|
+
children: "Active instances"
|
|
1734
|
+
}),
|
|
1735
|
+
/* @__PURE__ */ jsx(HoverHint, {
|
|
1736
|
+
text: "Counts only active instances — completed and aborted workflows are not included",
|
|
1737
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1738
|
+
muted: !0,
|
|
1739
|
+
size: 1,
|
|
1740
|
+
style: {
|
|
1741
|
+
opacity: .7
|
|
1742
|
+
},
|
|
1743
|
+
children: /* @__PURE__ */ jsx(InfoOutlineIcon, {})
|
|
1744
|
+
})
|
|
1745
|
+
}) ]
|
|
1746
|
+
})
|
|
1747
|
+
}),
|
|
1748
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1749
|
+
flex: COLUMNS.docTypes,
|
|
1750
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1751
|
+
muted: !0,
|
|
1752
|
+
size: 1,
|
|
1753
|
+
weight: "medium",
|
|
1754
|
+
children: "Document types"
|
|
1755
|
+
})
|
|
1756
|
+
}) ]
|
|
1757
|
+
})
|
|
1758
|
+
}),
|
|
1759
|
+
/* @__PURE__ */ jsx(Stack, {
|
|
1760
|
+
gap: 1,
|
|
1761
|
+
children: rows.map(row => /* @__PURE__ */ jsx(DefinitionRow, {
|
|
1762
|
+
countPending: instances.loading,
|
|
1763
|
+
onOpen: () => onOpenDefinition(row.name),
|
|
1764
|
+
row: row
|
|
1765
|
+
}, row.name))
|
|
1766
|
+
}) ]
|
|
1767
|
+
}), instances.truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1768
|
+
text: INSTANCE_CAP_COUNT_NOTE
|
|
1769
|
+
}) : null ]
|
|
1770
|
+
});
|
|
1771
|
+
}
|
|
1772
|
+
|
|
1773
|
+
function activeSummary(active) {
|
|
1774
|
+
return `${pluralize("active instance", active, !0)}`;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
const ROW_ISSUES_HINT = "This definition has issues";
|
|
1778
|
+
|
|
1779
|
+
function UndeployedWorkflows({notices: notices}) {
|
|
1780
|
+
return notices.length === 0 ? null :
|
|
1781
|
+
/* @__PURE__ */ jsx(Stack, {
|
|
1782
|
+
gap: 2,
|
|
1783
|
+
paddingBottom: 1,
|
|
1784
|
+
children: notices.map(notice => /* @__PURE__ */ jsx(CautionNote, {
|
|
1785
|
+
label: notice
|
|
1786
|
+
}, notice))
|
|
1787
|
+
});
|
|
1788
|
+
}
|
|
1789
|
+
|
|
1790
|
+
function DefinitionRow({countPending: countPending, onOpen: onOpen, row: row}) {
|
|
1791
|
+
const flagged = row.brokenBindings.length > 0, card = /* @__PURE__ */ jsx(Card, {
|
|
1792
|
+
as: "button",
|
|
1793
|
+
onClick: onOpen,
|
|
1794
|
+
paddingX: 2,
|
|
1795
|
+
paddingY: 3,
|
|
1796
|
+
radius: 2,
|
|
1797
|
+
style: {
|
|
1798
|
+
textAlign: "left",
|
|
1799
|
+
width: "100%"
|
|
1800
|
+
},
|
|
1801
|
+
...flagged ? {
|
|
1802
|
+
tone: "caution"
|
|
1803
|
+
} : {},
|
|
1804
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1805
|
+
align: "flex-start",
|
|
1806
|
+
gap: 4,
|
|
1807
|
+
children: [
|
|
1808
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1809
|
+
flex: COLUMNS.definition,
|
|
1810
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1811
|
+
align: "flex-start",
|
|
1812
|
+
gap: 3,
|
|
1813
|
+
paddingLeft: flagged ? 1 : 0,
|
|
1814
|
+
children: [ flagged ? /* @__PURE__ */ jsx(Text, {
|
|
1815
|
+
size: 1,
|
|
1816
|
+
children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
|
|
1817
|
+
}) : null,
|
|
1818
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
1819
|
+
flex: 1,
|
|
1820
|
+
gap: 3,
|
|
1821
|
+
style: {
|
|
1822
|
+
minWidth: 0
|
|
1823
|
+
},
|
|
1824
|
+
children: [
|
|
1825
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1826
|
+
size: 1,
|
|
1827
|
+
weight: "semibold",
|
|
1828
|
+
children: row.title
|
|
1829
|
+
}), row.description === void 0 ? /* @__PURE__ */ jsx(Text, {
|
|
1830
|
+
muted: !0,
|
|
1831
|
+
size: 1,
|
|
1832
|
+
children: /* @__PURE__ */ jsx("em", {
|
|
1833
|
+
children: "No description"
|
|
1834
|
+
})
|
|
1835
|
+
}) : /* @__PURE__ */ jsx(Text, {
|
|
1836
|
+
muted: !0,
|
|
1837
|
+
size: 1,
|
|
1838
|
+
children: row.description
|
|
1839
|
+
}) ]
|
|
1840
|
+
}) ]
|
|
1841
|
+
})
|
|
1842
|
+
}),
|
|
1843
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1844
|
+
flex: COLUMNS.instances,
|
|
1845
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
1846
|
+
muted: !0,
|
|
1847
|
+
size: 1,
|
|
1848
|
+
children: countPending ? "—" : `${row.activeCount} active`
|
|
1849
|
+
})
|
|
1850
|
+
}),
|
|
1851
|
+
/* @__PURE__ */ jsx(Col, {
|
|
1852
|
+
flex: COLUMNS.docTypes,
|
|
1853
|
+
children: /* @__PURE__ */ jsx(DocTypeChips, {
|
|
1854
|
+
docTypes: row.docTypes
|
|
1855
|
+
})
|
|
1856
|
+
}) ]
|
|
1857
|
+
})
|
|
1858
|
+
});
|
|
1859
|
+
return flagged ? /* @__PURE__ */ jsx(HoverHint, {
|
|
1860
|
+
fill: !0,
|
|
1861
|
+
text: ROW_ISSUES_HINT,
|
|
1862
|
+
children: card
|
|
1863
|
+
}) : card;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
function DocTypeChips({docTypes: docTypes}) {
|
|
1867
|
+
const schema = useSchema(), badgeTrim = useBadgeCapTrim();
|
|
1868
|
+
if (docTypes.length === 0) /* @__PURE__ */
|
|
1869
|
+
return jsx(Text, {
|
|
1870
|
+
muted: !0,
|
|
1871
|
+
size: 1,
|
|
1872
|
+
children: "—"
|
|
1873
|
+
});
|
|
1874
|
+
const labels = docTypeLabels(docTypes, name => schema.get(name)?.title), shown = labels.slice(0, MAX_VISIBLE_DOC_TYPES), overflow = labels.length - shown.length;
|
|
1875
|
+
/* @__PURE__ */
|
|
1876
|
+
return jsxs(Flex, {
|
|
1877
|
+
align: "center",
|
|
1878
|
+
gap: 2,
|
|
1879
|
+
style: badgeTrim,
|
|
1880
|
+
wrap: "wrap",
|
|
1881
|
+
children: [ shown.map(docType =>
|
|
1882
|
+
/* @__PURE__ */ jsx(Badge, {
|
|
1883
|
+
fontSize: 1,
|
|
1884
|
+
style: {
|
|
1885
|
+
minWidth: 0
|
|
1886
|
+
},
|
|
1887
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
1888
|
+
style: {
|
|
1889
|
+
display: "block",
|
|
1890
|
+
overflow: "hidden",
|
|
1891
|
+
textOverflow: "ellipsis",
|
|
1892
|
+
whiteSpace: "nowrap"
|
|
1893
|
+
},
|
|
1894
|
+
children: docType.title
|
|
1895
|
+
})
|
|
1896
|
+
}, docType.name)), overflow > 0 ? /* @__PURE__ */ jsxs(Text, {
|
|
1897
|
+
muted: !0,
|
|
1898
|
+
size: 1,
|
|
1899
|
+
children: [ "+", overflow, " more" ]
|
|
1900
|
+
}) : null ]
|
|
1901
|
+
});
|
|
1902
|
+
}
|
|
1903
|
+
|
|
1904
|
+
function normalizeAbortReason(raw) {
|
|
1905
|
+
const trimmed = raw.trim();
|
|
1906
|
+
return trimmed === "" ? void 0 : trimmed;
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
function abortedToast(title) {
|
|
1910
|
+
return {
|
|
1911
|
+
id: TOAST_ID.abort,
|
|
1912
|
+
status: "info",
|
|
1913
|
+
title: `Aborted “${title}”`
|
|
1914
|
+
};
|
|
1915
|
+
}
|
|
1916
|
+
|
|
1917
|
+
function abortAlreadySettledToast(title) {
|
|
1918
|
+
return {
|
|
1919
|
+
id: TOAST_ID.abort,
|
|
1920
|
+
status: "info",
|
|
1921
|
+
title: `“${title}” had already finished`,
|
|
1922
|
+
description: "Nothing was aborted — the workflow reached a terminal state first."
|
|
1923
|
+
};
|
|
1924
|
+
}
|
|
1925
|
+
|
|
1926
|
+
function abortUnconfirmedToast(args) {
|
|
1927
|
+
return {
|
|
1928
|
+
id: TOAST_ID.abort,
|
|
1929
|
+
status: "warning",
|
|
1930
|
+
title: `Couldn’t confirm “${args.title}” was aborted`,
|
|
1931
|
+
description: `${describeError(args.err)} — the abort may still have committed, so check the workflow’s state before trying again.`
|
|
1932
|
+
};
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
function AbortWorkflowDialog({entry: entry, onClose: onClose}) {
|
|
1936
|
+
const {engine: engine} = useWorkflowContext(), definition = useDefinition(entry), toast = useWorkflowToast(), [typedReason, setTypedReason] = useState(""), [pending, setPending] = useState(!1), {instance: instance} = entry, title = instanceTitle(instance, definition), reason = normalizeAbortReason(typedReason), reasonInputId = `abort-workflow-reason-${instance._id}`, confirm = async () => {
|
|
1937
|
+
if (reason !== void 0) {
|
|
1938
|
+
setPending(!0);
|
|
1939
|
+
try {
|
|
1940
|
+
const {changed: changed} = await engine.abortInstance({
|
|
1941
|
+
instanceId: instance._id,
|
|
1942
|
+
reason: reason
|
|
1943
|
+
});
|
|
1944
|
+
toast.push(changed ? abortedToast(title) : abortAlreadySettledToast(title)), onClose();
|
|
1945
|
+
} catch (err) {
|
|
1946
|
+
toast.push(abortUnconfirmedToast({
|
|
1947
|
+
title: title,
|
|
1948
|
+
err: err
|
|
1949
|
+
})), setPending(!1);
|
|
1950
|
+
}
|
|
1951
|
+
}
|
|
1952
|
+
};
|
|
1953
|
+
/* @__PURE__ */
|
|
1954
|
+
return jsx(Dialog, {
|
|
1955
|
+
footer: /* @__PURE__ */ jsx(Box, {
|
|
1956
|
+
padding: 3,
|
|
1957
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1958
|
+
gap: 2,
|
|
1959
|
+
justify: "flex-end",
|
|
1960
|
+
children: [
|
|
1961
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1962
|
+
disabled: pending,
|
|
1963
|
+
fontSize: 1,
|
|
1964
|
+
mode: "bleed",
|
|
1965
|
+
onClick: onClose,
|
|
1966
|
+
padding: 2,
|
|
1967
|
+
text: "Cancel"
|
|
1968
|
+
}),
|
|
1969
|
+
/* @__PURE__ */ jsx(Button, {
|
|
1970
|
+
disabled: reason === void 0,
|
|
1971
|
+
fontSize: 1,
|
|
1972
|
+
loading: pending,
|
|
1973
|
+
onClick: () => {
|
|
1974
|
+
confirm();
|
|
1975
|
+
},
|
|
1976
|
+
padding: 2,
|
|
1977
|
+
text: "Abort workflow",
|
|
1978
|
+
tone: "critical"
|
|
1979
|
+
}) ]
|
|
1980
|
+
})
|
|
1981
|
+
}),
|
|
1982
|
+
header: "Abort workflow",
|
|
1983
|
+
id: `abort-workflow-${instance._id}`,
|
|
1984
|
+
onClose: () => {
|
|
1985
|
+
pending || onClose();
|
|
1986
|
+
},
|
|
1987
|
+
width: 0,
|
|
1988
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
1989
|
+
padding: 4,
|
|
1990
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1991
|
+
gap: 5,
|
|
1992
|
+
children: [
|
|
1993
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
1994
|
+
size: 1,
|
|
1995
|
+
children: [ "Abort the ",
|
|
1996
|
+
/* @__PURE__ */ jsx("strong", {
|
|
1997
|
+
children: title
|
|
1998
|
+
}), " workflow? This cannot be undone." ]
|
|
1999
|
+
}),
|
|
2000
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
2001
|
+
gap: 3,
|
|
2002
|
+
children: [
|
|
2003
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2004
|
+
as: "label",
|
|
2005
|
+
htmlFor: reasonInputId,
|
|
2006
|
+
size: 1,
|
|
2007
|
+
weight: "medium",
|
|
2008
|
+
children: "Reason"
|
|
2009
|
+
}),
|
|
2010
|
+
/* @__PURE__ */ jsx(TextInput, {
|
|
2011
|
+
autoFocus: !0,
|
|
2012
|
+
fontSize: 1,
|
|
2013
|
+
id: reasonInputId,
|
|
2014
|
+
onChange: event => setTypedReason(event.currentTarget.value),
|
|
2015
|
+
placeholder: "Why is this workflow being stopped?",
|
|
2016
|
+
value: typedReason
|
|
2017
|
+
}) ]
|
|
2018
|
+
}) ]
|
|
2019
|
+
})
|
|
2020
|
+
})
|
|
2021
|
+
});
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
function AbortWorkflowSection({entry: entry}) {
|
|
2025
|
+
const [aborting, setAborting] = useState(!1);
|
|
2026
|
+
return isLiveEntry(entry) ? /* @__PURE__ */ jsxs(Stack, {
|
|
2027
|
+
gap: 4,
|
|
2028
|
+
children: [
|
|
2029
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2030
|
+
size: 1,
|
|
2031
|
+
weight: "semibold",
|
|
2032
|
+
children: "Manage workflow"
|
|
101
2033
|
}),
|
|
102
2034
|
/* @__PURE__ */ jsx(Flex, {
|
|
103
2035
|
children: /* @__PURE__ */ jsx(Button, {
|
|
104
2036
|
fontSize: 1,
|
|
105
|
-
icon: ArrowLeftIcon,
|
|
106
2037
|
mode: "ghost",
|
|
107
|
-
onClick:
|
|
2038
|
+
onClick: () => setAborting(!0),
|
|
108
2039
|
padding: 2,
|
|
109
|
-
text: "
|
|
2040
|
+
text: "Abort workflow…",
|
|
2041
|
+
tone: "critical"
|
|
2042
|
+
})
|
|
2043
|
+
}), aborting ? /* @__PURE__ */ jsx(AbortWorkflowDialog, {
|
|
2044
|
+
entry: entry,
|
|
2045
|
+
onClose: () => setAborting(!1)
|
|
2046
|
+
}) : null ]
|
|
2047
|
+
}) : null;
|
|
2048
|
+
}
|
|
2049
|
+
|
|
2050
|
+
function makeTitleResolver(definition) {
|
|
2051
|
+
const activities = (definition?.stages ?? []).flatMap(s => s.activities ?? []);
|
|
2052
|
+
return {
|
|
2053
|
+
action: (activityName, actionName) => activities.find(t => t.name === activityName)?.actions?.find(a => a.name === actionName)?.title ?? actionName,
|
|
2054
|
+
activity: name => activities.find(t => t.name === name)?.title ?? name,
|
|
2055
|
+
stage: name => stageTitle(definition, name)
|
|
2056
|
+
};
|
|
2057
|
+
}
|
|
2058
|
+
|
|
2059
|
+
function attributed(actor, ...parts) {
|
|
2060
|
+
if (actor) return {
|
|
2061
|
+
actorId: actor.id,
|
|
2062
|
+
parts: parts
|
|
2063
|
+
};
|
|
2064
|
+
const [first, ...rest] = parts;
|
|
2065
|
+
return typeof first != "string" ? {
|
|
2066
|
+
actorId: void 0,
|
|
2067
|
+
parts: parts
|
|
2068
|
+
} : {
|
|
2069
|
+
actorId: void 0,
|
|
2070
|
+
parts: [ first.charAt(0).toUpperCase() + first.slice(1), ...rest ]
|
|
2071
|
+
};
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
function standalone(...parts) {
|
|
2075
|
+
return {
|
|
2076
|
+
actorId: void 0,
|
|
2077
|
+
parts: parts
|
|
2078
|
+
};
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
const historyLiner = {
|
|
2082
|
+
stageEntered: (h, titles) => attributed(h.actor, `entered ${titles.stage(h.stage)}${h.fromStage ? ` from ${titles.stage(h.fromStage)}` : ""}`),
|
|
2083
|
+
stageExited: (h, titles) => attributed(h.actor, `left ${titles.stage(h.stage)} for ${titles.stage(h.toStage)}`),
|
|
2084
|
+
activityStatusChanged: (h, titles) => attributed(h.actor, `changed ${titles.activity(h.activity)}: ${h.from} → ${h.to}`),
|
|
2085
|
+
actionFired: (h, titles) => {
|
|
2086
|
+
const phrase = `fired ${titles.action(h.activity, h.action)} on ${titles.activity(h.activity)}`;
|
|
2087
|
+
return h.triggered ? standalone(`The workflow ${phrase}`) : attributed(h.actor, phrase);
|
|
2088
|
+
},
|
|
2089
|
+
transitionFired: (h, titles) => attributed(h.actor, `moved ${titles.stage(h.fromStage)} → ${titles.stage(h.toStage)}`),
|
|
2090
|
+
effectQueued: h => standalone("Effect ", {
|
|
2091
|
+
code: h.effect
|
|
2092
|
+
}, " queued"),
|
|
2093
|
+
effectCompleted: h => {
|
|
2094
|
+
const detail = h.detail ? ` — ${h.detail}` : "";
|
|
2095
|
+
return h.actor ? attributed(h.actor, "resolved effect ", {
|
|
2096
|
+
code: h.effect
|
|
2097
|
+
}, ` — ${h.status}${detail}`) : standalone("Effect ", {
|
|
2098
|
+
code: h.effect
|
|
2099
|
+
}, ` ${h.status}${detail}`);
|
|
2100
|
+
},
|
|
2101
|
+
effectClaimReleased: h => attributed(h.actor, "released a stale claim on effect ", {
|
|
2102
|
+
code: h.effect
|
|
2103
|
+
}, ` ${h.via === "sweep" ? "via the sweeper" : "via a drain takeover"}`),
|
|
2104
|
+
spawned: (h, titles) => attributed(h.actor, `spawned a child workflow from ${titles.activity(h.activity)}`),
|
|
2105
|
+
subworkflowAdopted: (h, titles) => standalone(`Re-adopted child workflow into ${titles.activity(h.activity)} on re-entering ${titles.stage(h.stage)}`),
|
|
2106
|
+
subworkflowResolved: (h, titles) => standalone(`Child workflow of ${titles.activity(h.activity)} ${h.status === "done" ? "completed" : "was aborted"}`),
|
|
2107
|
+
subworkflowOrphaned: h => standalone(`Orphaned child workflow reached terminal — ${h.detail}`),
|
|
2108
|
+
aborted: (h, titles) => attributed(h.actor, `aborted the workflow at ${titles.stage(h.stage)}${h.reason ? ` — ${h.reason}` : ""}`),
|
|
2109
|
+
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 ", {
|
|
2110
|
+
code: h.opType
|
|
2111
|
+
}),
|
|
2112
|
+
fieldQueryDiscarded: h => standalone({
|
|
2113
|
+
code: JSON.stringify(h)
|
|
2114
|
+
})
|
|
2115
|
+
};
|
|
2116
|
+
|
|
2117
|
+
function historyLine(h, titles) {
|
|
2118
|
+
return historyLiner[h._type](h, titles);
|
|
2119
|
+
}
|
|
2120
|
+
|
|
2121
|
+
function PendingEffectRows({instance: instance, now: now}) {
|
|
2122
|
+
const {drainEffectsFor: drainEffectsFor, completeEffectFor: completeEffectFor, effectHandlers: effectHandlers} = useWorkflowContext(), toast = useWorkflowToast(), [busy, setBusy] = useState(!1), pending = instance.pendingEffects ?? [];
|
|
2123
|
+
if (pending.length === 0) return null;
|
|
2124
|
+
const runRegistered = async () => {
|
|
2125
|
+
setBusy(!0);
|
|
2126
|
+
try {
|
|
2127
|
+
await drainEffectsFor(instance._id), toast.push({
|
|
2128
|
+
id: TOAST_ID.effectsDrain,
|
|
2129
|
+
status: "info",
|
|
2130
|
+
title: "Ran the registered handlers"
|
|
2131
|
+
});
|
|
2132
|
+
} catch (err) {
|
|
2133
|
+
toast.push({
|
|
2134
|
+
id: TOAST_ID.effectsDrain,
|
|
2135
|
+
status: "error",
|
|
2136
|
+
title: "Failed to run pending effects",
|
|
2137
|
+
description: describeError(err)
|
|
2138
|
+
});
|
|
2139
|
+
} finally {
|
|
2140
|
+
setBusy(!1);
|
|
2141
|
+
}
|
|
2142
|
+
}, resolve = async (effectKey, status) => {
|
|
2143
|
+
setBusy(!0);
|
|
2144
|
+
try {
|
|
2145
|
+
await completeEffectFor(instance._id, {
|
|
2146
|
+
effectKey: effectKey,
|
|
2147
|
+
status: status,
|
|
2148
|
+
detail: "Resolved in Studio"
|
|
2149
|
+
}), toast.push({
|
|
2150
|
+
id: TOAST_ID.effectResolve,
|
|
2151
|
+
status: "info",
|
|
2152
|
+
title: `Effect marked as ${status}`
|
|
2153
|
+
});
|
|
2154
|
+
} catch (err) {
|
|
2155
|
+
toast.push({
|
|
2156
|
+
id: TOAST_ID.effectResolve,
|
|
2157
|
+
status: "error",
|
|
2158
|
+
title: "Failed to resolve the effect",
|
|
2159
|
+
description: describeError(err)
|
|
2160
|
+
});
|
|
2161
|
+
} finally {
|
|
2162
|
+
setBusy(!1);
|
|
2163
|
+
}
|
|
2164
|
+
};
|
|
2165
|
+
/* @__PURE__ */
|
|
2166
|
+
return jsx(Fragment, {
|
|
2167
|
+
children: pending.map(fx => /* @__PURE__ */ jsx(PendingEffectRow, {
|
|
2168
|
+
busy: busy,
|
|
2169
|
+
effect: fx,
|
|
2170
|
+
instanceId: instance._id,
|
|
2171
|
+
now: now,
|
|
2172
|
+
onResolve: status => resolve(fx._key, status),
|
|
2173
|
+
onRunHandlers: runRegistered,
|
|
2174
|
+
registered: fx.name in effectHandlers
|
|
2175
|
+
}, fx._key))
|
|
2176
|
+
});
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
function PendingEffectRow({busy: busy, effect: effect, instanceId: instanceId, now: now, onResolve: onResolve, onRunHandlers: onRunHandlers, registered: registered}) {
|
|
2180
|
+
const label = effect.title ?? effect.name, avatarSize = useAvatarSize();
|
|
2181
|
+
/* @__PURE__ */
|
|
2182
|
+
return jsxs(Flex, {
|
|
2183
|
+
align: "center",
|
|
2184
|
+
gap: 2,
|
|
2185
|
+
children: [
|
|
2186
|
+
/* @__PURE__ */ jsx(FeedGlyph, {
|
|
2187
|
+
hint: "Waiting to run",
|
|
2188
|
+
icon: /* @__PURE__ */ jsx(ClockIcon, {})
|
|
2189
|
+
}),
|
|
2190
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
2191
|
+
align: "center",
|
|
2192
|
+
flex: 1,
|
|
2193
|
+
gap: 1,
|
|
2194
|
+
style: {
|
|
2195
|
+
minWidth: 0
|
|
2196
|
+
},
|
|
2197
|
+
children: [
|
|
2198
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2199
|
+
style: {
|
|
2200
|
+
minWidth: 0
|
|
2201
|
+
},
|
|
2202
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
2203
|
+
size: 1,
|
|
2204
|
+
textOverflow: "ellipsis",
|
|
2205
|
+
children: [ effect.title ?? /* @__PURE__ */ jsx(CodeChip, {
|
|
2206
|
+
value: effect.name
|
|
2207
|
+
}), registered ? " — ready to run in Studio" : " — waiting for the runtime" ]
|
|
2208
|
+
})
|
|
2209
|
+
}),
|
|
2210
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2211
|
+
muted: !0,
|
|
2212
|
+
size: 1,
|
|
2213
|
+
children: "·"
|
|
2214
|
+
}),
|
|
2215
|
+
/* @__PURE__ */ jsx(HoverHint, {
|
|
2216
|
+
text: formatDateTime(effect.queuedAt),
|
|
2217
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2218
|
+
muted: !0,
|
|
2219
|
+
size: 1,
|
|
2220
|
+
style: {
|
|
2221
|
+
whiteSpace: "nowrap"
|
|
2222
|
+
},
|
|
2223
|
+
children: formatShortAgo(effect.queuedAt, now)
|
|
2224
|
+
})
|
|
2225
|
+
}) ]
|
|
2226
|
+
}),
|
|
2227
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
2228
|
+
align: "center",
|
|
2229
|
+
flex: "none",
|
|
2230
|
+
style: {
|
|
2231
|
+
height: avatarSize
|
|
2232
|
+
},
|
|
2233
|
+
children: /* @__PURE__ */ jsx(HintedMenuButton, {
|
|
2234
|
+
button: /* @__PURE__ */ jsx(Button, {
|
|
2235
|
+
"aria-label": `Resolve ${label}`,
|
|
2236
|
+
disabled: busy,
|
|
2237
|
+
fontSize: 1,
|
|
2238
|
+
icon: EllipsisHorizontalIcon,
|
|
2239
|
+
mode: "bleed",
|
|
2240
|
+
padding: 2
|
|
2241
|
+
}),
|
|
2242
|
+
hint: "Resolve",
|
|
2243
|
+
id: `pending-effect-menu-${instanceId}-${effect._key}`,
|
|
2244
|
+
menu: /* @__PURE__ */ jsxs(Menu, {
|
|
2245
|
+
children: [ registered ? /* @__PURE__ */ jsx(MenuItem, {
|
|
2246
|
+
onClick: onRunHandlers,
|
|
2247
|
+
text: "Run registered handlers"
|
|
2248
|
+
}) : null,
|
|
2249
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
2250
|
+
onClick: () => onResolve("done"),
|
|
2251
|
+
text: "Mark done"
|
|
2252
|
+
}),
|
|
2253
|
+
/* @__PURE__ */ jsx(MenuItem, {
|
|
2254
|
+
onClick: () => onResolve("failed"),
|
|
2255
|
+
text: "Mark failed",
|
|
2256
|
+
tone: "critical"
|
|
2257
|
+
}) ]
|
|
2258
|
+
}),
|
|
2259
|
+
popover: {
|
|
2260
|
+
placement: "bottom-end",
|
|
2261
|
+
portal: !0
|
|
2262
|
+
}
|
|
2263
|
+
})
|
|
2264
|
+
}) ]
|
|
2265
|
+
});
|
|
2266
|
+
}
|
|
2267
|
+
|
|
2268
|
+
function ActivityLog({instance: instance, definition: definition}) {
|
|
2269
|
+
const titles = useMemo(() => makeTitleResolver(definition), [ definition ]), entries = useMemo(() => instance.history.toReversed(), [ instance.history ]), [now, setNow] = useState(() => /* @__PURE__ */ new Date);
|
|
2270
|
+
return useEffect(() => {
|
|
2271
|
+
const timer = setInterval(() => setNow(/* @__PURE__ */ new Date), 6e4);
|
|
2272
|
+
return () => clearInterval(timer);
|
|
2273
|
+
}, []), entries.length === 0 && (instance.pendingEffects ?? []).length === 0 ? /* @__PURE__ */ jsx(Text, {
|
|
2274
|
+
muted: !0,
|
|
2275
|
+
size: 1,
|
|
2276
|
+
children: "No activity yet"
|
|
2277
|
+
}) : /* @__PURE__ */ jsxs(Stack, {
|
|
2278
|
+
gap: 3,
|
|
2279
|
+
children: [
|
|
2280
|
+
/* @__PURE__ */ jsx(PendingEffectRows, {
|
|
2281
|
+
instance: instance,
|
|
2282
|
+
now: now
|
|
2283
|
+
}), entries.map(h => /* @__PURE__ */ jsx(HistoryRow, {
|
|
2284
|
+
entry: h,
|
|
2285
|
+
now: now,
|
|
2286
|
+
titles: titles
|
|
2287
|
+
}, h._key)) ]
|
|
2288
|
+
});
|
|
2289
|
+
}
|
|
2290
|
+
|
|
2291
|
+
function HistoryRow({entry: entry, now: now, titles: titles}) {
|
|
2292
|
+
const line = historyLine(entry, titles);
|
|
2293
|
+
/* @__PURE__ */
|
|
2294
|
+
return jsxs(Flex, {
|
|
2295
|
+
align: "center",
|
|
2296
|
+
gap: 2,
|
|
2297
|
+
children: [ line.actorId === void 0 ? /* @__PURE__ */ jsx(FeedGlyph, {
|
|
2298
|
+
hint: "The workflow",
|
|
2299
|
+
icon: /* @__PURE__ */ jsx(CogIcon, {})
|
|
2300
|
+
}) : /* @__PURE__ */ jsx(UserAvatar, {
|
|
2301
|
+
id: line.actorId
|
|
2302
|
+
}),
|
|
2303
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
2304
|
+
align: "center",
|
|
2305
|
+
flex: 1,
|
|
2306
|
+
gap: 1,
|
|
2307
|
+
style: {
|
|
2308
|
+
minWidth: 0
|
|
2309
|
+
},
|
|
2310
|
+
children: [
|
|
2311
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2312
|
+
style: {
|
|
2313
|
+
minWidth: 0
|
|
2314
|
+
},
|
|
2315
|
+
children: /* @__PURE__ */ jsxs(Text, {
|
|
2316
|
+
size: 1,
|
|
2317
|
+
textOverflow: "ellipsis",
|
|
2318
|
+
children: [ line.actorId === void 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
|
|
2319
|
+
children: [
|
|
2320
|
+
/* @__PURE__ */ jsx(ActorNameSpan, {
|
|
2321
|
+
id: line.actorId
|
|
2322
|
+
}), " " ]
|
|
2323
|
+
}), line.parts.map((part, index) => typeof part == "string" ? part : /* @__PURE__ */ jsx(CodeChip, {
|
|
2324
|
+
value: part.code
|
|
2325
|
+
}, index)) ]
|
|
2326
|
+
})
|
|
2327
|
+
}),
|
|
2328
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2329
|
+
muted: !0,
|
|
2330
|
+
size: 1,
|
|
2331
|
+
children: "·"
|
|
2332
|
+
}),
|
|
2333
|
+
/* @__PURE__ */ jsx(HoverHint, {
|
|
2334
|
+
text: formatDateTime(entry.at),
|
|
2335
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2336
|
+
muted: !0,
|
|
2337
|
+
size: 1,
|
|
2338
|
+
style: {
|
|
2339
|
+
whiteSpace: "nowrap"
|
|
2340
|
+
},
|
|
2341
|
+
children: formatShortAgo(entry.at, now)
|
|
2342
|
+
})
|
|
2343
|
+
}) ]
|
|
2344
|
+
}) ]
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
function ActorNameSpan({id: id}) {
|
|
2349
|
+
const {name: name} = useUserDisplay(id), {font: font} = useTheme_v2();
|
|
2350
|
+
/* @__PURE__ */
|
|
2351
|
+
return jsx("span", {
|
|
2352
|
+
style: {
|
|
2353
|
+
fontWeight: font.text.weights.medium
|
|
2354
|
+
},
|
|
2355
|
+
children: name
|
|
2356
|
+
});
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
function CodeChip({value: value}) {
|
|
2360
|
+
/* @__PURE__ */
|
|
2361
|
+
return jsx("code", {
|
|
2362
|
+
children: value
|
|
2363
|
+
});
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
function FeedGlyph({hint: hint, icon: icon}) {
|
|
2367
|
+
const size = useAvatarSize();
|
|
2368
|
+
/* @__PURE__ */
|
|
2369
|
+
return jsx(HoverHint, {
|
|
2370
|
+
text: hint,
|
|
2371
|
+
children: /* @__PURE__ */ jsx(Flex, {
|
|
2372
|
+
align: "center",
|
|
2373
|
+
flex: "none",
|
|
2374
|
+
justify: "center",
|
|
2375
|
+
style: {
|
|
2376
|
+
height: size,
|
|
2377
|
+
width: size
|
|
2378
|
+
},
|
|
2379
|
+
children: /* @__PURE__ */ jsx(Text, {
|
|
2380
|
+
muted: !0,
|
|
2381
|
+
size: 1,
|
|
2382
|
+
children: icon
|
|
2383
|
+
})
|
|
2384
|
+
})
|
|
2385
|
+
});
|
|
2386
|
+
}
|
|
2387
|
+
|
|
2388
|
+
function WorkflowInstanceDetail({instanceId: instanceId, backToList: backToList}) {
|
|
2389
|
+
const entry = useWorkflowInstanceEntry(instanceId);
|
|
2390
|
+
useLogEventOnMount(WorkflowInstanceDetailViewed, {
|
|
2391
|
+
instanceId: instanceId
|
|
2392
|
+
});
|
|
2393
|
+
const [graceOver, setGraceOver] = useState(!1);
|
|
2394
|
+
return useEffect(() => {
|
|
2395
|
+
const timer = setTimeout(() => setGraceOver(!0), 2500);
|
|
2396
|
+
return () => clearTimeout(timer);
|
|
2397
|
+
}, [ instanceId ]), entry?.invalid ? /* @__PURE__ */ jsx(Box, {
|
|
2398
|
+
padding: 4,
|
|
2399
|
+
children: /* @__PURE__ */ jsx(InvalidDocNotice, {
|
|
2400
|
+
invalid: entry.invalid
|
|
2401
|
+
})
|
|
2402
|
+
}) : entry ? /* @__PURE__ */ jsx(InstanceDetailPanel, {
|
|
2403
|
+
entry: entry
|
|
2404
|
+
}) : /* @__PURE__ */ jsx(Box, {
|
|
2405
|
+
paddingX: 3,
|
|
2406
|
+
children: /* @__PURE__ */ jsx(TabStatusRow, {
|
|
2407
|
+
children: graceOver ? /* @__PURE__ */ jsx(DetailNotFound, {
|
|
2408
|
+
back: backToList,
|
|
2409
|
+
message: /* @__PURE__ */ jsxs(Fragment, {
|
|
2410
|
+
children: [ "Unable to find workflow with id “",
|
|
2411
|
+
/* @__PURE__ */ jsx("strong", {
|
|
2412
|
+
children: instanceId
|
|
2413
|
+
}), "”." ]
|
|
2414
|
+
})
|
|
2415
|
+
}) : /* @__PURE__ */ jsx(LoadingRow, {
|
|
2416
|
+
label: "Loading workflow…"
|
|
110
2417
|
})
|
|
111
|
-
})
|
|
2418
|
+
})
|
|
112
2419
|
});
|
|
113
2420
|
}
|
|
114
2421
|
|
|
115
|
-
function InstanceDetailPanel({entry: entry
|
|
116
|
-
const definition = useDefinition(entry), [openActivity, setOpenActivity] = useState(null), {instance: instance} = entry;
|
|
2422
|
+
function InstanceDetailPanel({entry: entry}) {
|
|
2423
|
+
const definition = useDefinition(entry), snapshotWidth = useContainerToken(2), [openActivity, setOpenActivity] = useState(null), {instance: instance, ready: ready} = entry;
|
|
117
2424
|
return useEffect(() => {
|
|
118
2425
|
openActivity && openActivityGone({
|
|
119
2426
|
entry: entry,
|
|
@@ -124,36 +2431,56 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
|
|
|
124
2431
|
height: "fill",
|
|
125
2432
|
overflow: "hidden",
|
|
126
2433
|
children: [
|
|
127
|
-
/* @__PURE__ */ jsx(
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
2434
|
+
/* @__PURE__ */ jsx(Box, {
|
|
2435
|
+
flex: "none",
|
|
2436
|
+
paddingBottom: 2,
|
|
2437
|
+
paddingTop: 3,
|
|
2438
|
+
paddingX: 3,
|
|
2439
|
+
children: /* @__PURE__ */ jsx(DetailTitle, {
|
|
2440
|
+
busy: !ready || isEvaluationStale(entry),
|
|
2441
|
+
documentGdr: documentRefsOf(instance)[0],
|
|
2442
|
+
title: instanceTitle(instance, definition)
|
|
2443
|
+
})
|
|
131
2444
|
}),
|
|
132
2445
|
/* @__PURE__ */ jsx(Box, {
|
|
133
2446
|
flex: 1,
|
|
134
2447
|
overflow: "auto",
|
|
135
|
-
paddingX:
|
|
136
|
-
children: /* @__PURE__ */
|
|
137
|
-
gap: 4,
|
|
2448
|
+
paddingX: 3,
|
|
2449
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
138
2450
|
paddingBottom: 5,
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
2451
|
+
paddingLeft: 2,
|
|
2452
|
+
paddingTop: 2,
|
|
2453
|
+
style: {
|
|
2454
|
+
maxWidth: snapshotWidth
|
|
2455
|
+
},
|
|
2456
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
2457
|
+
gap: 6,
|
|
2458
|
+
children: [
|
|
2459
|
+
/* @__PURE__ */ jsx(InstanceSnapshotBody, {
|
|
2460
|
+
entry: entry,
|
|
2461
|
+
onOpenActivity: name => setOpenActivity({
|
|
2462
|
+
stage: instance.currentStage,
|
|
2463
|
+
activityName: name
|
|
2464
|
+
}),
|
|
2465
|
+
surface: "tool-instance-detail"
|
|
2466
|
+
}),
|
|
2467
|
+
/* @__PURE__ */ jsx(AbortWorkflowSection, {
|
|
2468
|
+
entry: entry
|
|
2469
|
+
}),
|
|
2470
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
2471
|
+
gap: 4,
|
|
2472
|
+
children: [
|
|
2473
|
+
/* @__PURE__ */ jsx(Text, {
|
|
2474
|
+
size: 1,
|
|
2475
|
+
weight: "semibold",
|
|
2476
|
+
children: "History"
|
|
2477
|
+
}),
|
|
2478
|
+
/* @__PURE__ */ jsx(ActivityLog, {
|
|
2479
|
+
definition: definition,
|
|
2480
|
+
instance: instance
|
|
2481
|
+
}) ]
|
|
2482
|
+
}) ]
|
|
2483
|
+
})
|
|
157
2484
|
})
|
|
158
2485
|
}), openActivity ? /* @__PURE__ */ jsx(ActivityDetailDialog, {
|
|
159
2486
|
activityName: openActivity.activityName,
|
|
@@ -166,130 +2493,14 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
|
|
|
166
2493
|
});
|
|
167
2494
|
}
|
|
168
2495
|
|
|
169
|
-
function DetailHeader({entry: entry, definition: definition, onBack: onBack}) {
|
|
170
|
-
const {instance: instance, ready: ready} = entry;
|
|
171
|
-
/* @__PURE__ */
|
|
172
|
-
return jsx(Card, {
|
|
173
|
-
flex: "none",
|
|
174
|
-
paddingX: 4,
|
|
175
|
-
paddingY: 3,
|
|
176
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
177
|
-
align: "center",
|
|
178
|
-
gap: 3,
|
|
179
|
-
wrap: "wrap",
|
|
180
|
-
children: [
|
|
181
|
-
/* @__PURE__ */ jsx(HoverHint, {
|
|
182
|
-
text: "Back to workflows",
|
|
183
|
-
children: /* @__PURE__ */ jsx(Button, {
|
|
184
|
-
"aria-label": "Back to workflows",
|
|
185
|
-
fontSize: 1,
|
|
186
|
-
icon: ArrowLeftIcon,
|
|
187
|
-
mode: "bleed",
|
|
188
|
-
onClick: onBack,
|
|
189
|
-
padding: 2
|
|
190
|
-
})
|
|
191
|
-
}),
|
|
192
|
-
/* @__PURE__ */ jsxs(Stack, {
|
|
193
|
-
gap: 2,
|
|
194
|
-
children: [
|
|
195
|
-
/* @__PURE__ */ jsx(Text, {
|
|
196
|
-
size: 1,
|
|
197
|
-
weight: "semibold",
|
|
198
|
-
children: instanceTitle(instance, definition)
|
|
199
|
-
}),
|
|
200
|
-
/* @__PURE__ */ jsxs(Text, {
|
|
201
|
-
muted: !0,
|
|
202
|
-
size: 1,
|
|
203
|
-
children: [ "Started ", formatDate(instance.startedAt) ]
|
|
204
|
-
}) ]
|
|
205
|
-
}), terminalState(instance) === "in-flight" ? /* @__PURE__ */ jsx(StageChip, {
|
|
206
|
-
title: stageTitle(definition, instance.currentStage)
|
|
207
|
-
}) : /* @__PURE__ */ jsx(AbortedBadge, {
|
|
208
|
-
instance: instance
|
|
209
|
-
}),
|
|
210
|
-
/* @__PURE__ */ jsx(SpinnerSlot, {
|
|
211
|
-
busy: !ready || isEvaluationStale(entry)
|
|
212
|
-
}) ]
|
|
213
|
-
})
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
function StudioDocumentsSection({instance: instance}) {
|
|
218
|
-
const refs = documentRefsOf(instance);
|
|
219
|
-
return refs.length === 0 ? null : /* @__PURE__ */ jsxs(Stack, {
|
|
220
|
-
gap: 3,
|
|
221
|
-
marginTop: 3,
|
|
222
|
-
children: [
|
|
223
|
-
/* @__PURE__ */ jsx(Text, {
|
|
224
|
-
muted: !0,
|
|
225
|
-
size: 1,
|
|
226
|
-
weight: "medium",
|
|
227
|
-
children: refs.length === 1 ? "Studio document" : "Studio documents"
|
|
228
|
-
}),
|
|
229
|
-
/* @__PURE__ */ jsx(Stack, {
|
|
230
|
-
gap: 2,
|
|
231
|
-
children: refs.map(ref => /* @__PURE__ */ jsx(DocPreviewLink, {
|
|
232
|
-
gdr: ref
|
|
233
|
-
}, ref.id))
|
|
234
|
-
}) ]
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function StageDiagramSection({entry: entry, definition: definition}) {
|
|
239
|
-
const {instance: instance} = entry;
|
|
240
|
-
return definition ?
|
|
241
|
-
/* @__PURE__ */ jsx(WorkflowDiagram, {
|
|
242
|
-
currentStage: terminalState(instance) === "in-flight" ? instance.currentStage : void 0,
|
|
243
|
-
definition: definition,
|
|
244
|
-
evaluation: entry.evaluation,
|
|
245
|
-
explain: !0,
|
|
246
|
-
guardCount: entry.guards?.length,
|
|
247
|
-
height: 340,
|
|
248
|
-
history: instance.history
|
|
249
|
-
}, instance._id) : /* @__PURE__ */ jsx(Card, {
|
|
250
|
-
border: !0,
|
|
251
|
-
marginTop: 3,
|
|
252
|
-
padding: 3,
|
|
253
|
-
radius: 2,
|
|
254
|
-
tone: "caution",
|
|
255
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
256
|
-
muted: !0,
|
|
257
|
-
size: 1,
|
|
258
|
-
children: "Couldn’t load this workflow’s full details — the stage diagram is unavailable, and technical names show instead of titles."
|
|
259
|
-
})
|
|
260
|
-
});
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
function MutedNote({text: text}) {
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
-
return jsx(Box, {
|
|
266
|
-
paddingY: 3,
|
|
267
|
-
children: /* @__PURE__ */ jsx(Text, {
|
|
268
|
-
muted: !0,
|
|
269
|
-
size: 1,
|
|
270
|
-
children: text
|
|
271
|
-
})
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
|
|
275
2496
|
function useStartableDefinitions() {
|
|
276
|
-
const {
|
|
277
|
-
return
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
description: row.description
|
|
284
|
-
}));
|
|
285
|
-
cancelled || (setDefinitions(startable), setError(void 0));
|
|
286
|
-
})().catch(err => {
|
|
287
|
-
cancelled || setError(describeError(err));
|
|
288
|
-
}), () => {
|
|
289
|
-
cancelled = !0;
|
|
290
|
-
};
|
|
291
|
-
}, [ engine ]), {
|
|
292
|
-
definitions: definitions,
|
|
2497
|
+
const {definitions: definitions, error: error} = useDeployedDefinitions();
|
|
2498
|
+
return {
|
|
2499
|
+
definitions: useMemo(() => definitions?.filter(row => isStartableDefinition(row)).map(row => ({
|
|
2500
|
+
name: row.name,
|
|
2501
|
+
title: row.title ?? row.name,
|
|
2502
|
+
description: row.description
|
|
2503
|
+
})), [ definitions ]),
|
|
293
2504
|
error: error
|
|
294
2505
|
};
|
|
295
2506
|
}
|
|
@@ -378,197 +2589,56 @@ function DefinitionPickerPanel({onPick: onPick}) {
|
|
|
378
2589
|
size: 1,
|
|
379
2590
|
textOverflow: "ellipsis",
|
|
380
2591
|
children: definition.description
|
|
381
|
-
}) : null ]
|
|
382
|
-
})
|
|
383
|
-
}, definition.name))
|
|
384
|
-
}) ]
|
|
385
|
-
})
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
function ToolActivityDialog({target: target, onClose: onClose}) {
|
|
390
|
-
const entry = useWorkflowInstanceEntry(target.instanceId), definition = useDefinition(entry), resolvedOnce = useRef(!1);
|
|
391
|
-
return entry && (resolvedOnce.current = !0), useEffect(() => {
|
|
392
|
-
resolvedOnce.current && openActivityGone({
|
|
393
|
-
entry: entry,
|
|
394
|
-
target: target
|
|
395
|
-
}) && onClose();
|
|
396
|
-
}, [ entry, target, onClose ]), entry?.invalid ? /* @__PURE__ */ jsx(Dialog, {
|
|
397
|
-
header: "Workflow unavailable",
|
|
398
|
-
id: "workflow-activity-detail",
|
|
399
|
-
onClose: onClose,
|
|
400
|
-
width: 1,
|
|
401
|
-
children: /* @__PURE__ */ jsx(Box, {
|
|
402
|
-
padding: 4,
|
|
403
|
-
children: /* @__PURE__ */ jsx(InvalidDocNotice, {
|
|
404
|
-
invalid: entry.invalid
|
|
405
|
-
})
|
|
406
|
-
})
|
|
407
|
-
}) : entry?.evaluation ? /* @__PURE__ */ jsx(ActivityDetailDialog, {
|
|
408
|
-
activityName: target.activityName,
|
|
409
|
-
breadcrumb: instanceBreadcrumb(entry.instance, definition),
|
|
410
|
-
definition: definition,
|
|
411
|
-
document: documentRefsOf(entry.instance)[0],
|
|
412
|
-
entry: entry,
|
|
413
|
-
onClose: onClose,
|
|
414
|
-
source: "tool-task-list"
|
|
415
|
-
}) : /* @__PURE__ */ jsx(Dialog, {
|
|
416
|
-
header: "Loading activity…",
|
|
417
|
-
id: "workflow-activity-detail",
|
|
418
|
-
onClose: onClose,
|
|
419
|
-
width: 1,
|
|
420
|
-
children: /* @__PURE__ */ jsx(Box, {
|
|
421
|
-
padding: 4,
|
|
422
|
-
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
423
|
-
label: "Fetching the live workflow state…"
|
|
424
|
-
})
|
|
425
|
-
})
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const GROUP_DEFAULT_OPEN = !0;
|
|
430
|
-
|
|
431
|
-
function instanceBandDefaultOpen(segment) {
|
|
432
|
-
return segment === "for-me";
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
const UNGROUPED_BAND_KEY = "section:no-document", PAYLOAD_VERSION = 1, MAX_ENTRIES = 200;
|
|
436
|
-
|
|
437
|
-
function bandStateStorageKey(scope) {
|
|
438
|
-
return `sanity.workflows.tool.bands:${scope}`;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function isRecord(value) {
|
|
442
|
-
return typeof value == "object" && value !== null && !Array.isArray(value);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
function bandEntryOf(value) {
|
|
446
|
-
if (!isRecord(value)) return;
|
|
447
|
-
const {open: open, touched: touched} = value;
|
|
448
|
-
if (!(typeof open != "boolean" || typeof touched != "number") && Number.isFinite(touched)) return {
|
|
449
|
-
open: open,
|
|
450
|
-
touched: touched
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
function parsePayload(raw) {
|
|
455
|
-
try {
|
|
456
|
-
const parsed = JSON.parse(raw);
|
|
457
|
-
if (!isRecord(parsed) || parsed.version !== PAYLOAD_VERSION || !isRecord(parsed.bands)) return;
|
|
458
|
-
const entries = [];
|
|
459
|
-
for (const [key, value] of Object.entries(parsed.bands)) {
|
|
460
|
-
const entry = bandEntryOf(value);
|
|
461
|
-
if (entry === void 0) return;
|
|
462
|
-
entries.push([ key, entry ]);
|
|
463
|
-
}
|
|
464
|
-
return Object.fromEntries(entries);
|
|
465
|
-
} catch {
|
|
466
|
-
return;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
|
|
470
|
-
function readStored(storage, storageKey) {
|
|
471
|
-
if (!storage) return {
|
|
472
|
-
entries: void 0,
|
|
473
|
-
corrupt: !1
|
|
474
|
-
};
|
|
475
|
-
let raw;
|
|
476
|
-
try {
|
|
477
|
-
raw = storage.getItem(storageKey);
|
|
478
|
-
} catch {
|
|
479
|
-
return {
|
|
480
|
-
entries: void 0,
|
|
481
|
-
corrupt: !1
|
|
482
|
-
};
|
|
483
|
-
}
|
|
484
|
-
if (raw === null) return {
|
|
485
|
-
entries: {},
|
|
486
|
-
corrupt: !1
|
|
487
|
-
};
|
|
488
|
-
const entries = parsePayload(raw);
|
|
489
|
-
return {
|
|
490
|
-
entries: entries,
|
|
491
|
-
corrupt: entries === void 0
|
|
492
|
-
};
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
function writeStored(args) {
|
|
496
|
-
if (!args.storage) return !1;
|
|
497
|
-
try {
|
|
498
|
-
return args.storage.setItem(args.storageKey, JSON.stringify({
|
|
499
|
-
version: PAYLOAD_VERSION,
|
|
500
|
-
bands: args.entries
|
|
501
|
-
})), !0;
|
|
502
|
-
} catch {
|
|
503
|
-
return !1;
|
|
504
|
-
}
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
function baseEntries(args) {
|
|
508
|
-
return args.stored === void 0 ? args.held : args.unpersisted ? mergeByTouched(args.stored, args.held) : args.stored;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function mergeByTouched(stored, held) {
|
|
512
|
-
const merged = {
|
|
513
|
-
...stored
|
|
514
|
-
};
|
|
515
|
-
for (const [key, entry] of Object.entries(held)) {
|
|
516
|
-
const persisted = merged[key];
|
|
517
|
-
(persisted === void 0 || entry.touched >= persisted.touched) && (merged[key] = entry);
|
|
518
|
-
}
|
|
519
|
-
return merged;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function prune(entries) {
|
|
523
|
-
const held = Object.entries(entries);
|
|
524
|
-
return held.length <= MAX_ENTRIES ? entries : Object.fromEntries(held.sort(([, a], [, b]) => b.touched - a.touched).slice(0, MAX_ENTRIES));
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function togglesOf(entries) {
|
|
528
|
-
return new Map(Object.entries(entries).map(([key, entry]) => [ key, entry.open ]));
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
function defaultStorage() {
|
|
532
|
-
try {
|
|
533
|
-
return globalThis.localStorage;
|
|
534
|
-
} catch {
|
|
535
|
-
return;
|
|
536
|
-
}
|
|
2592
|
+
}) : null ]
|
|
2593
|
+
})
|
|
2594
|
+
}, definition.name))
|
|
2595
|
+
}) ]
|
|
2596
|
+
})
|
|
2597
|
+
});
|
|
537
2598
|
}
|
|
538
2599
|
|
|
539
|
-
function
|
|
540
|
-
const
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
2600
|
+
function ToolActivityDialog({target: target, onClose: onClose}) {
|
|
2601
|
+
const entry = useWorkflowInstanceEntry(target.instanceId), definition = useDefinition(entry), resolvedOnce = useRef(!1);
|
|
2602
|
+
return entry && (resolvedOnce.current = !0), useEffect(() => {
|
|
2603
|
+
resolvedOnce.current && openActivityGone({
|
|
2604
|
+
entry: entry,
|
|
2605
|
+
target: target
|
|
2606
|
+
}) && onClose();
|
|
2607
|
+
}, [ entry, target, onClose ]), entry?.invalid ? /* @__PURE__ */ jsx(Dialog, {
|
|
2608
|
+
header: "Workflow unavailable",
|
|
2609
|
+
id: "workflow-activity-detail",
|
|
2610
|
+
onClose: onClose,
|
|
2611
|
+
width: 1,
|
|
2612
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
2613
|
+
padding: 4,
|
|
2614
|
+
children: /* @__PURE__ */ jsx(InvalidDocNotice, {
|
|
2615
|
+
invalid: entry.invalid
|
|
2616
|
+
})
|
|
2617
|
+
})
|
|
2618
|
+
}) : entry?.evaluation ? /* @__PURE__ */ jsx(ActivityDetailDialog, {
|
|
2619
|
+
activityName: target.activityName,
|
|
2620
|
+
breadcrumb: instanceBreadcrumb(entry.instance, definition),
|
|
2621
|
+
definition: definition,
|
|
2622
|
+
document: documentRefsOf(entry.instance)[0],
|
|
2623
|
+
entry: entry,
|
|
2624
|
+
onClose: onClose,
|
|
2625
|
+
source: "tool-task-list"
|
|
2626
|
+
}) : /* @__PURE__ */ jsx(Dialog, {
|
|
2627
|
+
header: "Loading activity…",
|
|
2628
|
+
id: "workflow-activity-detail",
|
|
2629
|
+
onClose: onClose,
|
|
2630
|
+
width: 1,
|
|
2631
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
2632
|
+
padding: 4,
|
|
2633
|
+
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
2634
|
+
label: "Fetching the live workflow state…"
|
|
2635
|
+
})
|
|
2636
|
+
})
|
|
545
2637
|
});
|
|
546
|
-
let held = initial.entries ?? {}, unpersisted = !1;
|
|
547
|
-
return {
|
|
548
|
-
read: () => togglesOf(held),
|
|
549
|
-
setOpen(key, open) {
|
|
550
|
-
const stored = readStored(storage, args.storageKey).entries, base = baseEntries({
|
|
551
|
-
stored: stored,
|
|
552
|
-
held: held,
|
|
553
|
-
unpersisted: unpersisted
|
|
554
|
-
});
|
|
555
|
-
return held = prune({
|
|
556
|
-
...base,
|
|
557
|
-
[key]: {
|
|
558
|
-
open: open,
|
|
559
|
-
touched: Date.now()
|
|
560
|
-
}
|
|
561
|
-
}), unpersisted = !writeStored({
|
|
562
|
-
storage: storage,
|
|
563
|
-
storageKey: args.storageKey,
|
|
564
|
-
entries: held
|
|
565
|
-
}), togglesOf(held);
|
|
566
|
-
}
|
|
567
|
-
};
|
|
568
2638
|
}
|
|
569
2639
|
|
|
570
2640
|
function datesOf(state) {
|
|
571
|
-
return state.filter(entry => entry._type
|
|
2641
|
+
return state.filter(entry => isDateFieldKind(entry._type)).map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
|
|
572
2642
|
}
|
|
573
2643
|
|
|
574
2644
|
function spawnOrigins(instances) {
|
|
@@ -668,6 +2738,14 @@ function deriveTaskGroups(args) {
|
|
|
668
2738
|
};
|
|
669
2739
|
}
|
|
670
2740
|
|
|
2741
|
+
function orderGroupsByPreviewTitle(groups, titles) {
|
|
2742
|
+
const withDocuments = [ ...groups.withDocuments ].sort(byResolvedTitle(group => titles.get(group.document.id), group => group.key));
|
|
2743
|
+
return {
|
|
2744
|
+
...groups,
|
|
2745
|
+
withDocuments: withDocuments
|
|
2746
|
+
};
|
|
2747
|
+
}
|
|
2748
|
+
|
|
671
2749
|
function cutTaskGroups(groups, cut) {
|
|
672
2750
|
const cutBands = bands => bands.filter(band => cut.keepBand?.(band) ?? !0).map(band => ({
|
|
673
2751
|
...band,
|
|
@@ -813,11 +2891,13 @@ function workflowTally(workflows) {
|
|
|
813
2891
|
function settleToast(outcome) {
|
|
814
2892
|
const total = outcome.settled + outcome.skipped.length + outcome.failures.length;
|
|
815
2893
|
if (outcome.skipped.length === 0 && outcome.failures.length === 0) return {
|
|
816
|
-
|
|
2894
|
+
id: TOAST_ID.orphanSettle,
|
|
2895
|
+
status: "info",
|
|
817
2896
|
title: outcome.settled === 1 ? "Cleaned up 1 workflow" : `Cleaned up ${outcome.settled} workflows`
|
|
818
2897
|
};
|
|
819
2898
|
const lines = [ ...outcome.skipped.map(workflow => `${workflow.title}: its document exists again — left running`), ...outcome.failures.map(failure => `${failure.workflow.title}: ${failure.message}`) ];
|
|
820
2899
|
return {
|
|
2900
|
+
id: TOAST_ID.orphanSettle,
|
|
821
2901
|
status: "warning",
|
|
822
2902
|
title: `Cleaned up ${outcome.settled} of ${total} workflows`,
|
|
823
2903
|
description: lines.join(`\n`)
|
|
@@ -826,9 +2906,10 @@ function settleToast(outcome) {
|
|
|
826
2906
|
|
|
827
2907
|
function recheckFailedToast(err) {
|
|
828
2908
|
return {
|
|
2909
|
+
id: TOAST_ID.orphanSettle,
|
|
829
2910
|
status: "error",
|
|
830
|
-
title: "Nothing was cleaned up
|
|
831
|
-
description: describeError(err)
|
|
2911
|
+
title: "Nothing was cleaned up",
|
|
2912
|
+
description: `The documents could not be re-checked: ${describeError(err)}`
|
|
832
2913
|
};
|
|
833
2914
|
}
|
|
834
2915
|
|
|
@@ -858,7 +2939,7 @@ function OrphanedWorkflowsNote({orphans: orphans}) {
|
|
|
858
2939
|
function SettleOrphansDialog({workflows: workflows, onClose: onClose}) {
|
|
859
2940
|
const {engine: engine} = useWorkflowContext(), client = useClient({
|
|
860
2941
|
apiVersion: WORKFLOW_API_VERSION
|
|
861
|
-
}), toast =
|
|
2942
|
+
}), toast = useWorkflowToast(), [pending, setPending] = useState(!1), confirm = async () => {
|
|
862
2943
|
setPending(!0);
|
|
863
2944
|
try {
|
|
864
2945
|
const outcome = await settleOrphans({
|
|
@@ -1103,38 +3184,6 @@ function applyTaskFilters(args) {
|
|
|
1103
3184
|
});
|
|
1104
3185
|
}
|
|
1105
3186
|
|
|
1106
|
-
const NOTHING = {
|
|
1107
|
-
titles: /* @__PURE__ */ new Map,
|
|
1108
|
-
resolved: /* @__PURE__ */ new Set
|
|
1109
|
-
};
|
|
1110
|
-
|
|
1111
|
-
function useDocPreviewTitles(documents) {
|
|
1112
|
-
const previewStore = useDocumentPreviewStore(), schema = useSchema(), {binding: binding} = useWorkflowContext(), [state, setState] = useState(NOTHING), key = documents.map(doc => doc.id).join("|");
|
|
1113
|
-
return useEffect(() => {
|
|
1114
|
-
const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, publish = () => setState({
|
|
1115
|
-
titles: new Map(titles),
|
|
1116
|
-
resolved: new Set(resolved)
|
|
1117
|
-
}), subscriptions = documents.flatMap(doc => {
|
|
1118
|
-
const {parsed: parsedRef, local: local} = gdrLocality(doc.id, binding.contentResource), schemaType = schema.get(doc.type);
|
|
1119
|
-
return !parsedRef || !local || !schemaType ? (resolved.add(doc.id), []) : [ previewStore.observeForPreview({
|
|
1120
|
-
_type: "reference",
|
|
1121
|
-
_ref: parsedRef.documentId
|
|
1122
|
-
}, schemaType).subscribe({
|
|
1123
|
-
next: event => {
|
|
1124
|
-
resolved.add(doc.id);
|
|
1125
|
-
const title = event.snapshot?.title;
|
|
1126
|
-
typeof title == "string" && titles.set(doc.id, title), publish();
|
|
1127
|
-
},
|
|
1128
|
-
error: err => {
|
|
1129
|
-
console.error(`[workflow-studio-plugin] preview for "${doc.id}" failed:`, err),
|
|
1130
|
-
resolved.add(doc.id), publish();
|
|
1131
|
-
}
|
|
1132
|
-
}) ];
|
|
1133
|
-
});
|
|
1134
|
-
return publish(), () => subscriptions.forEach(sub => sub.unsubscribe());
|
|
1135
|
-
}, [ key, previewStore, schema, binding.contentResource.id ]), state;
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
3187
|
const HITS_CAP = 6;
|
|
1139
3188
|
|
|
1140
3189
|
function TaskFilterMenu({filters: filters, options: options, onChange: onChange}) {
|
|
@@ -1633,8 +3682,10 @@ function FilterRowPortal({filters: filters, onChange: onChange, options: options
|
|
|
1633
3682
|
}), slot) : null;
|
|
1634
3683
|
}
|
|
1635
3684
|
|
|
1636
|
-
function ActivityDateControl({instanceId: instanceId, state: state, dates: dates}) {
|
|
1637
|
-
const
|
|
3685
|
+
function ActivityDateControl({instanceId: instanceId, state: state, surface: surface, dates: dates}) {
|
|
3686
|
+
const editField = useEditField(surface), {save: save} = useSaveField({
|
|
3687
|
+
instanceId: instanceId
|
|
3688
|
+
}), [open, setOpen] = useState(!1), raw = dateControlValue({
|
|
1638
3689
|
state: state,
|
|
1639
3690
|
dates: dates
|
|
1640
3691
|
}), display = raw === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
@@ -1657,15 +3708,16 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
|
|
|
1657
3708
|
})
|
|
1658
3709
|
});
|
|
1659
3710
|
const kind = dateControlKind(state), commit = (mode, value) => {
|
|
1660
|
-
state.kind === "editable" && save(() =>
|
|
1661
|
-
|
|
3711
|
+
state.kind === "editable" && save(() => editField({
|
|
3712
|
+
instanceId: instanceId,
|
|
3713
|
+
field: state.field,
|
|
1662
3714
|
...mode === "set" ? {
|
|
1663
3715
|
mode: mode,
|
|
1664
3716
|
value: value
|
|
1665
3717
|
} : {
|
|
1666
3718
|
mode: mode
|
|
1667
3719
|
}
|
|
1668
|
-
})
|
|
3720
|
+
}));
|
|
1669
3721
|
}, handleClick = e => {
|
|
1670
3722
|
e.stopPropagation(), setOpen(v => !v);
|
|
1671
3723
|
};
|
|
@@ -1682,9 +3734,9 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
|
|
|
1682
3734
|
setOpen(!1), commit("unset");
|
|
1683
3735
|
},
|
|
1684
3736
|
onPick: next => {
|
|
1685
|
-
kind
|
|
3737
|
+
hasTimeOfDay(kind) || setOpen(!1), commit("set", serializeDateFieldValue(next, kind));
|
|
1686
3738
|
},
|
|
1687
|
-
selectTime: kind
|
|
3739
|
+
selectTime: hasTimeOfDay(kind),
|
|
1688
3740
|
value: parseDateFieldValue(state.field.value, kind)
|
|
1689
3741
|
})
|
|
1690
3742
|
}),
|
|
@@ -1748,7 +3800,8 @@ function RefChips({refs: refs, max: max = 2}) {
|
|
|
1748
3800
|
wrap: "wrap",
|
|
1749
3801
|
children: [ shown.map(ref => /* @__PURE__ */ jsx(DocPreviewLink, {
|
|
1750
3802
|
gdr: ref,
|
|
1751
|
-
layout: "inline"
|
|
3803
|
+
layout: "inline",
|
|
3804
|
+
source: "doc-ref-chip"
|
|
1752
3805
|
}, ref.id)), overflow > 0 ? /* @__PURE__ */ jsxs(Text, {
|
|
1753
3806
|
muted: !0,
|
|
1754
3807
|
size: 1,
|
|
@@ -1757,57 +3810,18 @@ function RefChips({refs: refs, max: max = 2}) {
|
|
|
1757
3810
|
});
|
|
1758
3811
|
}
|
|
1759
3812
|
|
|
1760
|
-
const
|
|
3813
|
+
const UNGROUPED_TITLE = "Workflows without Studio documents";
|
|
1761
3814
|
|
|
1762
|
-
function TaskGroupList({
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
title: toBareId(group.document.id),
|
|
1773
|
-
header: /* @__PURE__ */ jsx(Flex, {
|
|
1774
|
-
align: "center",
|
|
1775
|
-
flex: "none",
|
|
1776
|
-
style: {
|
|
1777
|
-
maxWidth: "60%"
|
|
1778
|
-
},
|
|
1779
|
-
children: /* @__PURE__ */ jsx(RefChips, {
|
|
1780
|
-
max: 2,
|
|
1781
|
-
refs: group.documents
|
|
1782
|
-
})
|
|
1783
|
-
}),
|
|
1784
|
-
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
1785
|
-
bands: bands,
|
|
1786
|
-
instanceDefaultOpen: instanceDefaultOpen,
|
|
1787
|
-
instances: group.instances,
|
|
1788
|
-
onOpenTask: onOpenTask,
|
|
1789
|
-
onOpenWorkflow: onOpenWorkflow,
|
|
1790
|
-
showTerminalActions: showTerminalActions
|
|
1791
|
-
})
|
|
1792
|
-
}, group.key)), groups.withoutDocuments.length > 0 ? /* @__PURE__ */ jsx(CollapsibleBand, {
|
|
1793
|
-
background: !0,
|
|
1794
|
-
onToggle: () => bands.toggle(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
|
|
1795
|
-
open: bands.isOpen(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
|
|
1796
|
-
sticky: !0,
|
|
1797
|
-
title: UNGROUPED_TITLE,
|
|
1798
|
-
header: /* @__PURE__ */ jsx(Text, {
|
|
1799
|
-
size: 1,
|
|
1800
|
-
weight: "semibold",
|
|
1801
|
-
children: UNGROUPED_TITLE
|
|
1802
|
-
}),
|
|
1803
|
-
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
1804
|
-
bands: bands,
|
|
1805
|
-
instanceDefaultOpen: instanceDefaultOpen,
|
|
1806
|
-
instances: groups.withoutDocuments,
|
|
1807
|
-
onOpenTask: onOpenTask,
|
|
1808
|
-
onOpenWorkflow: onOpenWorkflow,
|
|
1809
|
-
showTerminalActions: showTerminalActions
|
|
1810
|
-
})
|
|
3815
|
+
function TaskGroupList({groups: groups, ...wiring}) {
|
|
3816
|
+
/* @__PURE__ */
|
|
3817
|
+
return jsxs(Stack, {
|
|
3818
|
+
gap: 1,
|
|
3819
|
+
children: [ groups.withDocuments.map(group => /* @__PURE__ */ jsx(DocumentBand, {
|
|
3820
|
+
group: group,
|
|
3821
|
+
...wiring
|
|
3822
|
+
}, group.key)), groups.withoutDocuments.length > 0 ? /* @__PURE__ */ jsx(UngroupedBand, {
|
|
3823
|
+
instances: groups.withoutDocuments,
|
|
3824
|
+
...wiring
|
|
1811
3825
|
}) : null ]
|
|
1812
3826
|
});
|
|
1813
3827
|
}
|
|
@@ -1820,38 +3834,81 @@ function targetOf(row) {
|
|
|
1820
3834
|
};
|
|
1821
3835
|
}
|
|
1822
3836
|
|
|
1823
|
-
function
|
|
3837
|
+
function DocumentBand({group: group, ...wiring}) {
|
|
3838
|
+
const {open: open, toggle: toggle} = useBandOpen(wiring.bands, group.key);
|
|
3839
|
+
/* @__PURE__ */
|
|
3840
|
+
return jsx(CollapsibleBand, {
|
|
3841
|
+
background: !0,
|
|
3842
|
+
onToggle: toggle,
|
|
3843
|
+
open: open,
|
|
3844
|
+
sticky: !0,
|
|
3845
|
+
title: toBareId(group.document.id),
|
|
3846
|
+
header: /* @__PURE__ */ jsx(Flex, {
|
|
3847
|
+
align: "center",
|
|
3848
|
+
flex: "none",
|
|
3849
|
+
style: {
|
|
3850
|
+
maxWidth: "60%"
|
|
3851
|
+
},
|
|
3852
|
+
children: /* @__PURE__ */ jsx(RefChips, {
|
|
3853
|
+
max: 2,
|
|
3854
|
+
refs: group.documents
|
|
3855
|
+
})
|
|
3856
|
+
}),
|
|
3857
|
+
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
3858
|
+
instances: group.instances,
|
|
3859
|
+
...wiring
|
|
3860
|
+
})
|
|
3861
|
+
});
|
|
3862
|
+
}
|
|
3863
|
+
|
|
3864
|
+
function UngroupedBand({instances: instances, ...wiring}) {
|
|
3865
|
+
const {open: open, toggle: toggle} = useBandOpen(wiring.bands, UNGROUPED_BAND_KEY);
|
|
3866
|
+
/* @__PURE__ */
|
|
3867
|
+
return jsx(CollapsibleBand, {
|
|
3868
|
+
background: !0,
|
|
3869
|
+
onToggle: toggle,
|
|
3870
|
+
open: open,
|
|
3871
|
+
sticky: !0,
|
|
3872
|
+
title: UNGROUPED_TITLE,
|
|
3873
|
+
header: /* @__PURE__ */ jsx(Text, {
|
|
3874
|
+
size: 1,
|
|
3875
|
+
weight: "semibold",
|
|
3876
|
+
children: UNGROUPED_TITLE
|
|
3877
|
+
}),
|
|
3878
|
+
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
3879
|
+
instances: instances,
|
|
3880
|
+
...wiring
|
|
3881
|
+
})
|
|
3882
|
+
});
|
|
3883
|
+
}
|
|
3884
|
+
|
|
3885
|
+
function InstanceBands({instances: instances, ...wiring}) {
|
|
1824
3886
|
/* @__PURE__ */
|
|
1825
3887
|
return jsx(Stack, {
|
|
1826
3888
|
gap: 1,
|
|
1827
|
-
children: instances.map(
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
onOpenTask: onOpenTask,
|
|
1832
|
-
onOpenWorkflow: onOpenWorkflow,
|
|
1833
|
-
showTerminalActions: showTerminalActions
|
|
1834
|
-
}, band.instanceId))
|
|
3889
|
+
children: instances.map(instance => /* @__PURE__ */ jsx(InstanceBand, {
|
|
3890
|
+
instance: instance,
|
|
3891
|
+
...wiring
|
|
3892
|
+
}, instance.instanceId))
|
|
1835
3893
|
});
|
|
1836
3894
|
}
|
|
1837
3895
|
|
|
1838
|
-
function InstanceBand({
|
|
1839
|
-
const {resolvePathFromState: resolvePathFromState} = useRouter();
|
|
3896
|
+
function InstanceBand({bands: bands, instance: instance, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
3897
|
+
const {open: open, toggle: toggle} = useBandOpen(bands, instance.instanceId), {resolvePathFromState: resolvePathFromState} = useRouter();
|
|
1840
3898
|
/* @__PURE__ */
|
|
1841
3899
|
return jsx(CollapsibleBand, {
|
|
1842
|
-
onToggle:
|
|
1843
|
-
open:
|
|
1844
|
-
title:
|
|
3900
|
+
onToggle: toggle,
|
|
3901
|
+
open: open,
|
|
3902
|
+
title: instance.title,
|
|
1845
3903
|
header: /* @__PURE__ */ jsxs(Fragment, {
|
|
1846
3904
|
children: [
|
|
1847
3905
|
/* @__PURE__ */ jsx(LinkChip, {
|
|
1848
3906
|
hint: "View workflow",
|
|
1849
3907
|
href: resolvePathFromState({
|
|
1850
|
-
instanceId:
|
|
3908
|
+
instanceId: instance.instanceId
|
|
1851
3909
|
}),
|
|
1852
3910
|
onClick: event => {
|
|
1853
|
-
event
|
|
1854
|
-
onOpenWorkflow(band.instanceId));
|
|
3911
|
+
isPlainClick(event) && (event.preventDefault(), onOpenWorkflow(instance.instanceId));
|
|
1855
3912
|
},
|
|
1856
3913
|
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1857
3914
|
align: "center",
|
|
@@ -1867,12 +3924,12 @@ function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDe
|
|
|
1867
3924
|
},
|
|
1868
3925
|
textOverflow: "ellipsis",
|
|
1869
3926
|
weight: "medium",
|
|
1870
|
-
children:
|
|
1871
|
-
}),
|
|
1872
|
-
segments: [
|
|
3927
|
+
children: instance.title
|
|
3928
|
+
}), instance.breadcrumb === void 0 ? null : /* @__PURE__ */ jsx(BreadcrumbTail, {
|
|
3929
|
+
segments: [ instance.breadcrumb ]
|
|
1873
3930
|
}),
|
|
1874
3931
|
/* @__PURE__ */ jsx(StageFace, {
|
|
1875
|
-
title:
|
|
3932
|
+
title: instance.stageTitle
|
|
1876
3933
|
}) ]
|
|
1877
3934
|
})
|
|
1878
3935
|
}),
|
|
@@ -1882,7 +3939,7 @@ function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDe
|
|
|
1882
3939
|
paddingLeft: 5,
|
|
1883
3940
|
children: /* @__PURE__ */ jsx(TaskRows, {
|
|
1884
3941
|
onOpenTask: onOpenTask,
|
|
1885
|
-
rows:
|
|
3942
|
+
rows: instance.rows,
|
|
1886
3943
|
showTerminalActions: showTerminalActions
|
|
1887
3944
|
})
|
|
1888
3945
|
})
|
|
@@ -1916,40 +3973,19 @@ function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTer
|
|
|
1916
3973
|
dateControl: /* @__PURE__ */ jsx(ActivityDateControl, {
|
|
1917
3974
|
dates: render.dates,
|
|
1918
3975
|
instanceId: row.instanceId,
|
|
1919
|
-
state: render.dateState
|
|
3976
|
+
state: render.dateState,
|
|
3977
|
+
surface: "tool-task-list"
|
|
1920
3978
|
}),
|
|
1921
3979
|
face: render.face,
|
|
1922
3980
|
instanceId: row.instanceId,
|
|
1923
3981
|
onOpen: onOpen,
|
|
3982
|
+
surface: "tool-task-list",
|
|
1924
3983
|
terminalActions: showTerminalActions ? render.terminalActions : []
|
|
1925
3984
|
})
|
|
1926
3985
|
})
|
|
1927
3986
|
});
|
|
1928
3987
|
}
|
|
1929
3988
|
|
|
1930
|
-
function useBandOpenState(args) {
|
|
1931
|
-
const {segment: segment, scope: scope} = args, store = useMemo(() => createBandStateStore({
|
|
1932
|
-
storageKey: bandStateStorageKey(scope)
|
|
1933
|
-
}), [ scope ]), [held, setHeld] = useState(() => ({
|
|
1934
|
-
store: store,
|
|
1935
|
-
toggles: store.read()
|
|
1936
|
-
}));
|
|
1937
|
-
held.store !== store && setHeld({
|
|
1938
|
-
store: store,
|
|
1939
|
-
toggles: store.read()
|
|
1940
|
-
});
|
|
1941
|
-
const qualify = key => `${segment}/${key}`, isOpen = (key, defaultOpen) => held.toggles.get(qualify(key)) ?? defaultOpen;
|
|
1942
|
-
return {
|
|
1943
|
-
isOpen: isOpen,
|
|
1944
|
-
toggle: (key, defaultOpen) => {
|
|
1945
|
-
setHeld({
|
|
1946
|
-
store: store,
|
|
1947
|
-
toggles: store.setOpen(qualify(key), !isOpen(key, defaultOpen))
|
|
1948
|
-
});
|
|
1949
|
-
}
|
|
1950
|
-
};
|
|
1951
|
-
}
|
|
1952
|
-
|
|
1953
3989
|
const NO_MISSING = /* @__PURE__ */ new Set;
|
|
1954
3990
|
|
|
1955
3991
|
function sameIdSet(a, b) {
|
|
@@ -1982,28 +4018,6 @@ function useOrphanedWorkflows(groups) {
|
|
|
1982
4018
|
}), [ groups, contentResource, missing ]);
|
|
1983
4019
|
}
|
|
1984
4020
|
|
|
1985
|
-
const INSTANCE_CAP = 200, NEWEST_INSTANCES = {
|
|
1986
|
-
includeCompleted: !0,
|
|
1987
|
-
limit: INSTANCE_CAP
|
|
1988
|
-
};
|
|
1989
|
-
|
|
1990
|
-
function useToolInstances() {
|
|
1991
|
-
const {engine: engine} = useWorkflowContext(), {instances: instances, loading: loading, unreadable: unreadable} = useWorkflowInstances({
|
|
1992
|
-
engine: engine,
|
|
1993
|
-
filter: NEWEST_INSTANCES
|
|
1994
|
-
});
|
|
1995
|
-
return useMemo(() => {
|
|
1996
|
-
const rows = instances ?? [];
|
|
1997
|
-
return {
|
|
1998
|
-
inFlight: rows.filter(instance => terminalState(instance) === "in-flight"),
|
|
1999
|
-
settled: rows.filter(instance => terminalState(instance) !== "in-flight"),
|
|
2000
|
-
truncated: rows.length + unreadable.length === INSTANCE_CAP,
|
|
2001
|
-
loading: loading,
|
|
2002
|
-
unreadable: unreadable
|
|
2003
|
-
};
|
|
2004
|
-
}, [ instances, loading, unreadable ]);
|
|
2005
|
-
}
|
|
2006
|
-
|
|
2007
4021
|
function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, segment: segment, truncated: truncated, unreadable: unreadable}) {
|
|
2008
4022
|
const mineOnly = segment === "for-me", {binding: binding} = useWorkflowContext(), bands = useBandOpenState({
|
|
2009
4023
|
segment: segment,
|
|
@@ -2011,21 +4025,38 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
2011
4025
|
}), [filters, setFilters] = useState({}), groups = useMemo(() => deriveTaskGroups({
|
|
2012
4026
|
instances: instances,
|
|
2013
4027
|
identity: identity
|
|
2014
|
-
}), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = useMemo(() => taskFilterOptions(listed), [ listed ]),
|
|
4028
|
+
}), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = useMemo(() => taskFilterOptions(listed), [ listed ]), titles = useGroupTitles(listed), revealed = useRevealGate({
|
|
4029
|
+
loading: loading,
|
|
4030
|
+
ready: titles.ready
|
|
4031
|
+
}), visible = useMemo(() => {
|
|
2015
4032
|
const filtered = applyTaskFilters({
|
|
2016
4033
|
groups: listed,
|
|
2017
4034
|
filters: filters,
|
|
2018
4035
|
now: /* @__PURE__ */ new Date
|
|
2019
|
-
});
|
|
2020
|
-
return
|
|
2021
|
-
}, [ listed, filters, mineOnly ]);
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
paddingLeft: 2,
|
|
4036
|
+
}), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
|
|
4037
|
+
return orderGroupsByPreviewTitle(cut, titles.titles);
|
|
4038
|
+
}, [ listed, filters, mineOnly, titles ]);
|
|
4039
|
+
if (loading || !revealed) /* @__PURE__ */
|
|
4040
|
+
return jsx(TabStatusRow, {
|
|
2025
4041
|
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
2026
4042
|
label: "Loading tasks…"
|
|
2027
4043
|
})
|
|
2028
|
-
})
|
|
4044
|
+
});
|
|
4045
|
+
const empty = visible.withDocuments.length === 0 && visible.withoutDocuments.length === 0;
|
|
4046
|
+
let body;
|
|
4047
|
+
return empty ? mineOnly ? body = /* @__PURE__ */ jsx(ForMeEmptyState, {}) : body = /* @__PURE__ */ jsx(EmptyState, {
|
|
4048
|
+
description: "Adjust the filters, or start a workflow",
|
|
4049
|
+
icon: /* @__PURE__ */ jsx(SearchIcon, {}),
|
|
4050
|
+
title: "No tasks match"
|
|
4051
|
+
}) : body = /* @__PURE__ */ jsx(TaskGroupList, {
|
|
4052
|
+
bands: bands,
|
|
4053
|
+
groups: visible,
|
|
4054
|
+
onOpenTask: onOpenTask,
|
|
4055
|
+
onOpenWorkflow: onOpenWorkflow,
|
|
4056
|
+
showTerminalActions: mineOnly
|
|
4057
|
+
}), /* @__PURE__ */ jsxs(Flex, {
|
|
4058
|
+
direction: "column",
|
|
4059
|
+
flex: 1,
|
|
2029
4060
|
children: [
|
|
2030
4061
|
/* @__PURE__ */ jsx(FilterRowPortal, {
|
|
2031
4062
|
filters: filters,
|
|
@@ -2033,91 +4064,133 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
2033
4064
|
options: options,
|
|
2034
4065
|
slot: filterSlot
|
|
2035
4066
|
}),
|
|
2036
|
-
/* @__PURE__ */ jsxs(
|
|
4067
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
4068
|
+
direction: "column",
|
|
4069
|
+
flex: 1,
|
|
2037
4070
|
gap: 2,
|
|
4071
|
+
paddingBottom: 4,
|
|
2038
4072
|
children: [
|
|
2039
4073
|
/* @__PURE__ */ jsx(UnreadableDocsNote, {
|
|
2040
4074
|
unreadable: unreadable
|
|
2041
4075
|
}),
|
|
2042
4076
|
/* @__PURE__ */ jsx(OrphanedWorkflowsNote, {
|
|
2043
4077
|
orphans: orphans
|
|
2044
|
-
}),
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
instanceDefaultOpen: instanceBandDefaultOpen(segment),
|
|
2049
|
-
onOpenTask: onOpenTask,
|
|
2050
|
-
onOpenWorkflow: onOpenWorkflow,
|
|
2051
|
-
showTerminalActions: mineOnly
|
|
2052
|
-
}) ]
|
|
2053
|
-
}), truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
2054
|
-
text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren't listed.`
|
|
2055
|
-
}) : null ]
|
|
4078
|
+
}), body, truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
4079
|
+
text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren’t listed.`
|
|
4080
|
+
}) : null ]
|
|
4081
|
+
}) ]
|
|
2056
4082
|
});
|
|
2057
4083
|
}
|
|
2058
4084
|
|
|
2059
|
-
const
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
4085
|
+
const TOOL_TAB_LABELS = {
|
|
4086
|
+
definitions: "Definitions",
|
|
4087
|
+
documents: "Documents",
|
|
4088
|
+
tasks: "Tasks",
|
|
4089
|
+
"for-me": "For me"
|
|
4090
|
+
}, TOOL_TAB_VALUES = Object.keys(TOOL_TAB_LABELS), TOOL_TABS = TOOL_TAB_VALUES.map(value => ({
|
|
4091
|
+
value: value,
|
|
4092
|
+
label: TOOL_TAB_LABELS[value]
|
|
4093
|
+
})), DEFAULT_TOOL_TAB = "definitions";
|
|
4094
|
+
|
|
4095
|
+
function toolTabState(tab) {
|
|
4096
|
+
return tab === DEFAULT_TOOL_TAB ? {} : {
|
|
4097
|
+
workflowsTab: tab
|
|
4098
|
+
};
|
|
4099
|
+
}
|
|
2066
4100
|
|
|
2067
|
-
function
|
|
2068
|
-
const router = useRouter(), params = Object.fromEntries(router.state._searchParams ?? []);
|
|
4101
|
+
function boardState(definitionName) {
|
|
2069
4102
|
return {
|
|
2070
|
-
|
|
2071
|
-
setTab: next => router.navigate({
|
|
2072
|
-
_searchParams: definedEntries(TOOL_TAB_CODEC.write(params, next))
|
|
2073
|
-
})
|
|
4103
|
+
boardDefinition: definitionName
|
|
2074
4104
|
};
|
|
2075
4105
|
}
|
|
2076
4106
|
|
|
2077
|
-
function
|
|
2078
|
-
return
|
|
4107
|
+
function tabSelectionNavigates(route, tab) {
|
|
4108
|
+
return !(route.kind === "home" && tab === route.tab);
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
function backToAllLabel(tab) {
|
|
4112
|
+
return `Back to all ${TOOL_TAB_LABELS[tab].toLowerCase()}`;
|
|
2079
4113
|
}
|
|
2080
4114
|
|
|
2081
|
-
function
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
4115
|
+
function toolRoute(state) {
|
|
4116
|
+
const {instanceId: instanceId, definitionName: definitionName, boardDefinition: boardDefinition} = state;
|
|
4117
|
+
if (typeof instanceId == "string") return {
|
|
4118
|
+
kind: "instance",
|
|
4119
|
+
instanceId: instanceId,
|
|
4120
|
+
tab: "tasks"
|
|
4121
|
+
};
|
|
4122
|
+
if (typeof definitionName == "string") return {
|
|
4123
|
+
kind: "definition",
|
|
4124
|
+
definitionName: definitionName,
|
|
4125
|
+
tab: "definitions"
|
|
4126
|
+
};
|
|
4127
|
+
if (typeof boardDefinition == "string") return {
|
|
4128
|
+
kind: "home",
|
|
4129
|
+
tab: "documents",
|
|
4130
|
+
boardDefinition: boardDefinition
|
|
4131
|
+
};
|
|
4132
|
+
const tab = pathSegmentTab({
|
|
4133
|
+
segment: state.workflowsTab,
|
|
4134
|
+
tabs: TOOL_TAB_VALUES,
|
|
4135
|
+
fallback: DEFAULT_TOOL_TAB
|
|
4136
|
+
});
|
|
4137
|
+
return tab === void 0 ? {
|
|
4138
|
+
kind: "not-found",
|
|
4139
|
+
tab: void 0
|
|
4140
|
+
} : {
|
|
4141
|
+
kind: "home",
|
|
4142
|
+
tab: tab,
|
|
4143
|
+
boardDefinition: void 0
|
|
2085
4144
|
};
|
|
2086
4145
|
}
|
|
2087
4146
|
|
|
4147
|
+
function InstanceStreamKeepAlive() {
|
|
4148
|
+
return useToolInstances(), null;
|
|
4149
|
+
}
|
|
4150
|
+
|
|
2088
4151
|
function WorkflowsToolRoot() {
|
|
2089
|
-
const router = useRouter(), {seedInstance: seedInstance} = useWorkflowContext(),
|
|
2090
|
-
|
|
2091
|
-
|
|
4152
|
+
const router = useRouter(), {seedInstance: seedInstance} = useWorkflowContext(), telemetry = useWorkflowTelemetry(), [filterSlot, setFilterSlot] = useState(null), route = toolRoute(router.state), isHome = route.kind === "home" || route.kind === "not-found", [instanceStreamLatched, setInstanceStreamLatched] = useState(isHome);
|
|
4153
|
+
useEffect(() => {
|
|
4154
|
+
isHome && setInstanceStreamLatched(!0);
|
|
4155
|
+
}, [ isHome ]);
|
|
4156
|
+
const setTab = next => router.navigate(toolTabState(next)), selectBoardWorkflow = useCallback((name, opts) => router.navigate(boardState(name), opts?.replace === !0 ? {
|
|
4157
|
+
replace: !0
|
|
4158
|
+
} : {}), [ router ]), backToList = tab => ({
|
|
4159
|
+
label: backToAllLabel(tab),
|
|
4160
|
+
go: () => setTab(tab)
|
|
4161
|
+
}), badSegment = route.kind === "not-found";
|
|
4162
|
+
useEffect(() => {
|
|
4163
|
+
badSegment && router.navigate({}, {
|
|
4164
|
+
replace: !0
|
|
4165
|
+
});
|
|
4166
|
+
}, [ badSegment, router ]), useLogEventOnMount(WorkflowToolOpened, {
|
|
4167
|
+
tab: route.tab ?? "not-found",
|
|
2092
4168
|
via: "open"
|
|
2093
4169
|
});
|
|
2094
|
-
|
|
2095
|
-
return
|
|
2096
|
-
instanceId: instanceId,
|
|
2097
|
-
|
|
2098
|
-
}, instanceId) : /* @__PURE__ */ jsx(
|
|
4170
|
+
let panel;
|
|
4171
|
+
return route.kind === "instance" ? panel = /* @__PURE__ */ jsx(WorkflowInstanceDetail, {
|
|
4172
|
+
instanceId: route.instanceId,
|
|
4173
|
+
backToList: backToList(route.tab)
|
|
4174
|
+
}, route.instanceId) : route.kind === "definition" ? panel = /* @__PURE__ */ jsx(DefinitionDetail, {
|
|
4175
|
+
definitionName: route.definitionName,
|
|
4176
|
+
backToList: backToList(route.tab)
|
|
4177
|
+
}, route.definitionName) : route.kind === "not-found" ? panel = null : panel = /* @__PURE__ */ jsx(WorkflowsHome, {
|
|
4178
|
+
boardDefinition: route.boardDefinition,
|
|
4179
|
+
filterSlot: filterSlot,
|
|
4180
|
+
onOpenDefinition: name => router.navigate({
|
|
4181
|
+
definitionName: name
|
|
4182
|
+
}),
|
|
2099
4183
|
onOpenInstance: instance => {
|
|
2100
|
-
typeof instance != "string" && seedInstance(instance), router.navigate(
|
|
4184
|
+
typeof instance != "string" && seedInstance(instance), router.navigate({
|
|
2101
4185
|
instanceId: typeof instance == "string" ? instance : instance._id
|
|
2102
|
-
}
|
|
2103
|
-
}
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
2108
|
-
const {tab: tab, setTab: setTab} = useToolTab(), telemetry = useWorkflowTelemetry(), instances = useToolInstances(), identity = useAssignmentIdentity(), [openTask, setOpenTask] = useState(null), openInstance = target => {
|
|
2109
|
-
if (typeof target != "string") return onOpenInstance(target);
|
|
2110
|
-
const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
|
|
2111
|
-
onOpenInstance(held ?? target);
|
|
2112
|
-
}, [filterSlot, setFilterSlot] = useState(null), scrollerRef = useRef(null);
|
|
2113
|
-
return useEffect(() => {
|
|
2114
|
-
scrollerRef.current?.scrollTo({
|
|
2115
|
-
top: 0
|
|
2116
|
-
});
|
|
2117
|
-
}, [ tab ]),
|
|
4186
|
+
});
|
|
4187
|
+
},
|
|
4188
|
+
onSelectWorkflow: selectBoardWorkflow,
|
|
4189
|
+
tab: route.tab
|
|
4190
|
+
}),
|
|
2118
4191
|
/* @__PURE__ */ jsxs(Card, {
|
|
2119
4192
|
height: "fill",
|
|
2120
|
-
children: [
|
|
4193
|
+
children: [ instanceStreamLatched ? /* @__PURE__ */ jsx(InstanceStreamKeepAlive, {}) : null,
|
|
2121
4194
|
/* @__PURE__ */ jsxs(Flex, {
|
|
2122
4195
|
direction: "column",
|
|
2123
4196
|
height: "fill",
|
|
@@ -2126,75 +4199,103 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
|
2126
4199
|
/* @__PURE__ */ jsx(Box, {
|
|
2127
4200
|
flex: "none",
|
|
2128
4201
|
padding: 3,
|
|
2129
|
-
children: /* @__PURE__ */
|
|
2130
|
-
|
|
2131
|
-
children:
|
|
4202
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
4203
|
+
gap: 3,
|
|
4204
|
+
children: [
|
|
4205
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
4206
|
+
align: "center",
|
|
2132
4207
|
gap: 3,
|
|
4208
|
+
paddingLeft: 2,
|
|
2133
4209
|
children: [
|
|
2134
|
-
/* @__PURE__ */
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
children: [
|
|
2139
|
-
/* @__PURE__ */ jsx(Text, {
|
|
2140
|
-
size: 2,
|
|
2141
|
-
weight: "semibold",
|
|
2142
|
-
children: "Workflows"
|
|
2143
|
-
}),
|
|
2144
|
-
/* @__PURE__ */ jsx(Box, {
|
|
2145
|
-
flex: 1
|
|
2146
|
-
}),
|
|
2147
|
-
/* @__PURE__ */ jsx(NewWorkflowButton, {}) ]
|
|
4210
|
+
/* @__PURE__ */ jsx(Text, {
|
|
4211
|
+
size: 1,
|
|
4212
|
+
weight: "semibold",
|
|
4213
|
+
children: "Workflows"
|
|
2148
4214
|
}),
|
|
2149
|
-
/* @__PURE__ */ jsx(
|
|
2150
|
-
|
|
2151
|
-
idPrefix: "workflows-tool-tab",
|
|
2152
|
-
onSelect: next => {
|
|
2153
|
-
next !== tab && telemetry.log(WorkflowToolOpened, {
|
|
2154
|
-
tab: next,
|
|
2155
|
-
via: "tab-switch"
|
|
2156
|
-
}), setTab(next);
|
|
2157
|
-
},
|
|
2158
|
-
options: TOOL_TABS,
|
|
2159
|
-
selected: tab
|
|
4215
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4216
|
+
flex: 1
|
|
2160
4217
|
}),
|
|
2161
|
-
/* @__PURE__ */ jsx(
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
4218
|
+
/* @__PURE__ */ jsx(NewWorkflowButton, {}) ]
|
|
4219
|
+
}),
|
|
4220
|
+
/* @__PURE__ */ jsx(TabSwitch, {
|
|
4221
|
+
ariaControls: "workflows-tool-panel",
|
|
4222
|
+
idPrefix: "workflows-tool-tab",
|
|
4223
|
+
onSelect: next => {
|
|
4224
|
+
tabSelectionNavigates(route, next) && (next !== route.tab && telemetry.log(WorkflowToolOpened, {
|
|
4225
|
+
tab: next,
|
|
4226
|
+
via: "tab-switch"
|
|
4227
|
+
}), setTab(next));
|
|
4228
|
+
},
|
|
4229
|
+
options: TOOL_TABS,
|
|
4230
|
+
selected: route.tab
|
|
4231
|
+
}),
|
|
4232
|
+
/* @__PURE__ */ jsx("div", {
|
|
4233
|
+
ref: setFilterSlot
|
|
4234
|
+
}) ]
|
|
2165
4235
|
})
|
|
2166
4236
|
}),
|
|
2167
4237
|
/* @__PURE__ */ jsx(Box, {
|
|
2168
|
-
ref: scrollerRef,
|
|
2169
4238
|
flex: 1,
|
|
2170
|
-
overflow: "
|
|
2171
|
-
|
|
4239
|
+
overflow: "hidden",
|
|
4240
|
+
children: panel
|
|
4241
|
+
}) ]
|
|
4242
|
+
}) ]
|
|
4243
|
+
});
|
|
4244
|
+
}
|
|
4245
|
+
|
|
4246
|
+
function WorkflowsHome({boardDefinition: boardDefinition, filterSlot: filterSlot, onOpenDefinition: onOpenDefinition, onOpenInstance: onOpenInstance, onSelectWorkflow: onSelectWorkflow, tab: tab}) {
|
|
4247
|
+
const instances = useToolInstances(), identity = useAssignmentIdentity(), [openTask, setOpenTask] = useState(null), openInstance = target => {
|
|
4248
|
+
if (typeof target != "string") return onOpenInstance(target);
|
|
4249
|
+
const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
|
|
4250
|
+
onOpenInstance(held ?? target);
|
|
4251
|
+
}, scrollerRef = useRef(null);
|
|
4252
|
+
useEffect(() => {
|
|
4253
|
+
scrollerRef.current?.scrollTo({
|
|
4254
|
+
top: 0
|
|
4255
|
+
});
|
|
4256
|
+
}, [ tab ]);
|
|
4257
|
+
const bleeds = tab === "documents";
|
|
4258
|
+
/* @__PURE__ */
|
|
4259
|
+
return jsxs(Flex, {
|
|
4260
|
+
direction: "column",
|
|
4261
|
+
height: "fill",
|
|
4262
|
+
overflow: "hidden",
|
|
4263
|
+
children: [
|
|
4264
|
+
/* @__PURE__ */ jsx(Box, {
|
|
4265
|
+
ref: scrollerRef,
|
|
4266
|
+
flex: 1,
|
|
4267
|
+
overflow: "auto",
|
|
4268
|
+
paddingX: bleeds ? 0 : TOOL_PANEL_PADDING,
|
|
4269
|
+
style: {
|
|
4270
|
+
scrollbarGutter: bleeds ? "auto" : "stable"
|
|
4271
|
+
},
|
|
4272
|
+
children: /* @__PURE__ */ jsx(TabPanel, {
|
|
4273
|
+
"aria-labelledby": `workflows-tool-tab-${tab}`,
|
|
4274
|
+
id: "workflows-tool-panel",
|
|
2172
4275
|
style: {
|
|
2173
|
-
|
|
4276
|
+
display: "flex",
|
|
4277
|
+
flexDirection: "column",
|
|
4278
|
+
minHeight: "100%"
|
|
2174
4279
|
},
|
|
2175
|
-
children: /* @__PURE__ */ jsx(
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
truncated: instances.truncated,
|
|
2192
|
-
unreadable: instances.unreadable
|
|
2193
|
-
})
|
|
2194
|
-
})
|
|
4280
|
+
children: /* @__PURE__ */ jsx(Flex, {
|
|
4281
|
+
direction: "column",
|
|
4282
|
+
flex: 1,
|
|
4283
|
+
style: {
|
|
4284
|
+
minHeight: 0
|
|
4285
|
+
},
|
|
4286
|
+
children: /* @__PURE__ */ jsx(HomePanel, {
|
|
4287
|
+
boardDefinition: boardDefinition,
|
|
4288
|
+
filterSlot: filterSlot,
|
|
4289
|
+
identity: identity,
|
|
4290
|
+
instances: instances,
|
|
4291
|
+
onOpenDefinition: onOpenDefinition,
|
|
4292
|
+
onOpenTask: setOpenTask,
|
|
4293
|
+
onOpenWorkflow: openInstance,
|
|
4294
|
+
onSelectWorkflow: onSelectWorkflow,
|
|
4295
|
+
tab: tab
|
|
2195
4296
|
})
|
|
2196
4297
|
})
|
|
2197
|
-
})
|
|
4298
|
+
})
|
|
2198
4299
|
}), openTask ? /* @__PURE__ */ jsx(ToolActivityDialog, {
|
|
2199
4300
|
onClose: () => setOpenTask(null),
|
|
2200
4301
|
target: openTask
|
|
@@ -2202,4 +4303,25 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
|
2202
4303
|
});
|
|
2203
4304
|
}
|
|
2204
4305
|
|
|
4306
|
+
function HomePanel({boardDefinition: boardDefinition, filterSlot: filterSlot, identity: identity, instances: instances, onOpenDefinition: onOpenDefinition, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, onSelectWorkflow: onSelectWorkflow, tab: tab}) {
|
|
4307
|
+
return tab === "definitions" ? /* @__PURE__ */ jsx(DefinitionsTab, {
|
|
4308
|
+
onOpenDefinition: onOpenDefinition
|
|
4309
|
+
}) : tab === "documents" ? /* @__PURE__ */ jsx(DocumentsTab, {
|
|
4310
|
+
filterSlot: filterSlot,
|
|
4311
|
+
onOpenDefinition: onOpenDefinition,
|
|
4312
|
+
onSelectWorkflow: onSelectWorkflow,
|
|
4313
|
+
routeDefinition: boardDefinition
|
|
4314
|
+
}) : /* @__PURE__ */ jsx(TasksTab, {
|
|
4315
|
+
filterSlot: filterSlot,
|
|
4316
|
+
identity: identity,
|
|
4317
|
+
instances: [ ...instances.inFlight, ...instances.settled ],
|
|
4318
|
+
loading: instances.loading,
|
|
4319
|
+
onOpenTask: onOpenTask,
|
|
4320
|
+
onOpenWorkflow: onOpenWorkflow,
|
|
4321
|
+
segment: tab,
|
|
4322
|
+
truncated: instances.truncated,
|
|
4323
|
+
unreadable: instances.unreadable
|
|
4324
|
+
});
|
|
4325
|
+
}
|
|
4326
|
+
|
|
2205
4327
|
export { WorkflowsToolRoot as default };
|