@sanity/workflow-studio-plugin 0.5.0 → 0.21.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 +232 -0
- package/README.md +45 -63
- package/dist/_chunks-cjs/index.cjs +1046 -502
- package/dist/_chunks-cjs/workflows-tool-root.cjs +551 -59
- package/dist/_chunks-es/index.js +1064 -522
- package/dist/_chunks-es/workflows-tool-root.js +556 -62
- package/dist/index.cjs +0 -6
- package/dist/index.d.cts +16 -74
- package/dist/index.d.ts +16 -74
- package/dist/index.js +2 -2
- package/package.json +16 -9
|
@@ -1,23 +1,27 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { Stack, Box, Flex, Text, Button, Card, TextInput, Dialog, Badge, useTheme_v2, Container, TabPanel } from "@sanity/ui";
|
|
4
|
+
|
|
5
|
+
import { useWorkflowTelemetry, editFieldTarget } from "@sanity/workflow-react";
|
|
4
6
|
|
|
5
7
|
import { useState, useEffect, useMemo, useRef } from "react";
|
|
6
8
|
|
|
7
9
|
import { useRouter } from "sanity/router";
|
|
8
10
|
|
|
9
|
-
import { instanceNotice, StaleLock, ActivitiesList, TodoItemsList, isEvaluationStale, useWorkflowInstanceEntry, InvalidDocNotice, LoadingRow, useDefinition, openActivityGone, GroupHeading, ActivityDetailDialog, instanceBreadcrumb, HoverHint, instanceTitle, formatDate, StageChip, stageTitle, AbortedBadge, DocPreviewLink, useWorkflowContext, readDeployedDefinitions, describeError, DismissablePopover, definitionSnapshotOf, findActivity, stateByActivity, findActivityNode, rowDateControlState, rowAssignControlState, isActivityAssignedTo, assigneeUserIdsOf, isOpenActivityStatus, committedRowFace, activityRowProps,
|
|
11
|
+
import { instanceNotice, StaleLock, ActivitiesList, TodoItemsList, isEvaluationStale, useWorkflowInstanceEntry, useLogEventOnMount, WorkflowInstanceDetailViewed, InvalidDocNotice, LoadingRow, useDefinition, openActivityGone, GroupHeading, ActivityDetailDialog, instanceBreadcrumb, HoverHint, instanceTitle, formatDate, StageChip, stageTitle, AbortedBadge, SpinnerSlot, DocPreviewLink, useWorkflowContext, readDeployedDefinitions, describeError, DismissablePopover, definitionSnapshotOf, findActivity, stateByActivity, findActivityNode, rowDateControlState, rowAssignControlState, isActivityAssignedTo, assigneeUserIdsOf, isOpenActivityStatus, committedRowFace, activityRowProps, gdrLocality, CautionNote, WORKFLOW_API_VERSION, useClosableToast, parseStoredDateValue, useUserDisplays, useProjectMembers, WorkflowTaskFiltersApplied, DocRefFace, useSaveField, dateControlValue, stopRowMouseDown, stopRowClick, parseDateFieldValue, serializeDateFieldValue, dateControlKind, CollapsibleBand, LinkChip, BreadcrumbTail, StageFace, TrailingHairline, ActivityRow, UnreadableDocsNote, useAssignmentIdentity, TabSwitch, WorkflowToolOpened, workflowsTabCodec } from "./index.js";
|
|
10
12
|
|
|
11
13
|
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
12
14
|
|
|
13
15
|
import { WorkflowDiagram } from "@sanity/workflow-diagram";
|
|
14
16
|
|
|
15
|
-
import { entryDocRefs, terminalState, latestDeployedDefinitions, isStartableDefinition,
|
|
17
|
+
import { entryDocRefs, terminalState, latestDeployedDefinitions, isStartableDefinition, toBareId, findOpenStageEntry } from "@sanity/workflow-engine";
|
|
16
18
|
|
|
17
19
|
import { AddIcon } from "@sanity/icons/Add";
|
|
18
20
|
|
|
19
21
|
import { SearchIcon } from "@sanity/icons/Search";
|
|
20
22
|
|
|
23
|
+
import { useClient, useDocumentPreviewStore, useSchema } from "sanity";
|
|
24
|
+
|
|
21
25
|
import { CheckmarkIcon } from "@sanity/icons/Checkmark";
|
|
22
26
|
|
|
23
27
|
import { CloseIcon } from "@sanity/icons/Close";
|
|
@@ -30,28 +34,10 @@ import { endOfDay } from "date-fns/endOfDay";
|
|
|
30
34
|
|
|
31
35
|
import { createPortal } from "react-dom";
|
|
32
36
|
|
|
33
|
-
import { useDocumentPreviewStore, useSchema } from "sanity";
|
|
34
|
-
|
|
35
37
|
import { CalendarIcon } from "@sanity/icons/Calendar";
|
|
36
38
|
|
|
37
39
|
import { useWorkflowInstances } from "@sanity/workflow-studio";
|
|
38
40
|
|
|
39
|
-
function SpinnerSlot({busy: busy}) {
|
|
40
|
-
/* @__PURE__ */
|
|
41
|
-
return jsx(Flex, {
|
|
42
|
-
align: "center",
|
|
43
|
-
flex: "none",
|
|
44
|
-
justify: "center",
|
|
45
|
-
style: {
|
|
46
|
-
width: 19,
|
|
47
|
-
height: 19
|
|
48
|
-
},
|
|
49
|
-
children: busy ? /* @__PURE__ */ jsx(Spinner, {
|
|
50
|
-
muted: !0
|
|
51
|
-
}) : null
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
|
|
55
41
|
function documentRefsOf(instance) {
|
|
56
42
|
const seen = /* @__PURE__ */ new Map;
|
|
57
43
|
for (const ref of entryDocRefs(instance.fields)) seen.has(ref.id) || seen.set(ref.id, ref);
|
|
@@ -68,14 +54,19 @@ function InstanceTaskLists({entry: entry, onOpenActivity: onOpenActivity}) {
|
|
|
68
54
|
onOpenActivity: onOpenActivity
|
|
69
55
|
}),
|
|
70
56
|
/* @__PURE__ */ jsx(TodoItemsList, {
|
|
71
|
-
entry: entry
|
|
57
|
+
entry: entry,
|
|
58
|
+
surface: "tool-instance-detail"
|
|
72
59
|
}) ]
|
|
73
60
|
})
|
|
74
61
|
});
|
|
75
62
|
}
|
|
76
63
|
|
|
77
64
|
function WorkflowInstanceDetail({instanceId: instanceId, onBack: onBack}) {
|
|
78
|
-
const entry = useWorkflowInstanceEntry(instanceId)
|
|
65
|
+
const entry = useWorkflowInstanceEntry(instanceId);
|
|
66
|
+
useLogEventOnMount(WorkflowInstanceDetailViewed, {
|
|
67
|
+
instanceId: instanceId
|
|
68
|
+
});
|
|
69
|
+
const [graceOver, setGraceOver] = useState(!1);
|
|
79
70
|
return useEffect(() => {
|
|
80
71
|
const timer = setTimeout(() => setGraceOver(!0), 2500);
|
|
81
72
|
return () => clearTimeout(timer);
|
|
@@ -169,7 +160,8 @@ function InstanceDetailPanel({entry: entry, onBack: onBack}) {
|
|
|
169
160
|
breadcrumb: instanceBreadcrumb(instance, definition),
|
|
170
161
|
definition: definition,
|
|
171
162
|
entry: entry,
|
|
172
|
-
onClose: () => setOpenActivity(null)
|
|
163
|
+
onClose: () => setOpenActivity(null),
|
|
164
|
+
source: "tool-instance-detail"
|
|
173
165
|
}) : null ]
|
|
174
166
|
});
|
|
175
167
|
}
|
|
@@ -310,7 +302,8 @@ function NewWorkflowButton() {
|
|
|
310
302
|
onPick: definition => {
|
|
311
303
|
setOpen(!1), openStartDialog({
|
|
312
304
|
definition: definition.name,
|
|
313
|
-
label: definition.title
|
|
305
|
+
label: definition.title,
|
|
306
|
+
source: "tool"
|
|
314
307
|
});
|
|
315
308
|
}
|
|
316
309
|
}) : null,
|
|
@@ -417,7 +410,8 @@ function ToolActivityDialog({target: target, onClose: onClose}) {
|
|
|
417
410
|
definition: definition,
|
|
418
411
|
document: documentRefsOf(entry.instance)[0],
|
|
419
412
|
entry: entry,
|
|
420
|
-
onClose: onClose
|
|
413
|
+
onClose: onClose,
|
|
414
|
+
source: "tool-task-list"
|
|
421
415
|
}) : /* @__PURE__ */ jsx(Dialog, {
|
|
422
416
|
header: "Loading activity…",
|
|
423
417
|
id: "workflow-activity-detail",
|
|
@@ -432,13 +426,154 @@ function ToolActivityDialog({target: target, onClose: onClose}) {
|
|
|
432
426
|
});
|
|
433
427
|
}
|
|
434
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
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function createBandStateStore(args) {
|
|
540
|
+
const storage = args.storage ?? defaultStorage(), initial = readStored(storage, args.storageKey);
|
|
541
|
+
initial.corrupt && writeStored({
|
|
542
|
+
storage: storage,
|
|
543
|
+
storageKey: args.storageKey,
|
|
544
|
+
entries: {}
|
|
545
|
+
});
|
|
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
|
+
}
|
|
569
|
+
|
|
435
570
|
function datesOf(state) {
|
|
436
571
|
return state.filter(entry => entry._type === "date" || entry._type === "datetime").map(entry => entry.value).filter(value => typeof value == "string" && value !== "");
|
|
437
572
|
}
|
|
438
573
|
|
|
439
574
|
function spawnOrigins(instances) {
|
|
440
575
|
const origins = /* @__PURE__ */ new Map;
|
|
441
|
-
for (const parent of instances) for (const row of parent.subworkflows ?? []) origins.set(
|
|
576
|
+
for (const parent of instances) for (const row of parent.subworkflows ?? []) origins.set(toBareId(row.ref.id), {
|
|
442
577
|
parent: parent,
|
|
443
578
|
activityName: row.activity,
|
|
444
579
|
cohortStage: row.cohortStage
|
|
@@ -448,7 +583,7 @@ function spawnOrigins(instances) {
|
|
|
448
583
|
|
|
449
584
|
function rootOf(instance, byId) {
|
|
450
585
|
const rootRef = instance.ancestors[0];
|
|
451
|
-
return rootRef ? byId.get(
|
|
586
|
+
return rootRef ? byId.get(toBareId(rootRef.id)) ?? instance : instance;
|
|
452
587
|
}
|
|
453
588
|
|
|
454
589
|
function taskRowsOf(args) {
|
|
@@ -589,6 +724,223 @@ function taskRowRender(row, entry) {
|
|
|
589
724
|
};
|
|
590
725
|
}
|
|
591
726
|
|
|
727
|
+
const NO_ORPHANS = {
|
|
728
|
+
groupKeys: /* @__PURE__ */ new Set,
|
|
729
|
+
workflows: []
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
function localBareIds(group, contentResource) {
|
|
733
|
+
const ids = group.documents.map(doc => gdrLocality(doc.id, contentResource));
|
|
734
|
+
if (!ids.some(locality => !locality.parsed || !locality.local)) return ids.flatMap(locality => locality.parsed ? [ locality.parsed.documentId ] : []);
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
function orphanProbeIds(groups, contentResource) {
|
|
738
|
+
const ids = /* @__PURE__ */ new Set;
|
|
739
|
+
for (const group of groups.withDocuments) for (const id of localBareIds(group, contentResource) ?? []) ids.add(id);
|
|
740
|
+
return [ ...ids ].sort();
|
|
741
|
+
}
|
|
742
|
+
|
|
743
|
+
function orphanVerdict(args) {
|
|
744
|
+
const groupKeys = /* @__PURE__ */ new Set, workflows = /* @__PURE__ */ new Map;
|
|
745
|
+
for (const group of args.groups.withDocuments) {
|
|
746
|
+
const ids = localBareIds(group, args.contentResource);
|
|
747
|
+
if (!(ids === void 0 || !ids.every(id => args.missing.has(id)))) {
|
|
748
|
+
groupKeys.add(group.key);
|
|
749
|
+
for (const band of group.instances) workflows.set(band.instanceId, {
|
|
750
|
+
instanceId: band.instanceId,
|
|
751
|
+
title: band.title,
|
|
752
|
+
documentIds: ids
|
|
753
|
+
});
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
return groupKeys.size === 0 ? NO_ORPHANS : {
|
|
757
|
+
groupKeys: groupKeys,
|
|
758
|
+
workflows: [ ...workflows.values() ]
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function hideOrphanedGroups(groups, orphaned) {
|
|
763
|
+
return orphaned.size === 0 ? groups : cutTaskGroups(groups, {
|
|
764
|
+
keepRow: () => !0,
|
|
765
|
+
keepGroup: group => !orphaned.has(group.key)
|
|
766
|
+
});
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
const DRAFT_PREFIX = "drafts.";
|
|
770
|
+
|
|
771
|
+
async function missingDocIds(client, ids) {
|
|
772
|
+
const present = await client.fetch("*[_id in $ids || _id in $drafts]._id", {
|
|
773
|
+
ids: ids,
|
|
774
|
+
drafts: ids.map(id => `${DRAFT_PREFIX}${id}`)
|
|
775
|
+
}, {
|
|
776
|
+
perspective: "raw"
|
|
777
|
+
}), found = new Set(present.map(id => id.startsWith(DRAFT_PREFIX) ? id.slice(DRAFT_PREFIX.length) : id)), candidates = ids.filter(id => !found.has(id)), versions = await Promise.all(candidates.map(id => client.fetch("*[_id in path($version)][0]._id", {
|
|
778
|
+
version: `versions.*.${id}`
|
|
779
|
+
}, {
|
|
780
|
+
perspective: "raw"
|
|
781
|
+
})));
|
|
782
|
+
return new Set(candidates.filter((_, index) => versions[index] === null));
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
async function settleOrphans(args) {
|
|
786
|
+
const ids = [ ...new Set(args.workflows.flatMap(workflow => workflow.documentIds)) ].sort(), missing = await args.probeMissing(ids), stillOrphaned = workflow => workflow.documentIds.every(id => missing.has(id));
|
|
787
|
+
let settled = 0;
|
|
788
|
+
const failures = [];
|
|
789
|
+
for (const workflow of args.workflows.filter(stillOrphaned)) try {
|
|
790
|
+
await args.abort({
|
|
791
|
+
instanceId: workflow.instanceId,
|
|
792
|
+
reason: "Subject document deleted"
|
|
793
|
+
}), settled += 1;
|
|
794
|
+
} catch (err) {
|
|
795
|
+
failures.push({
|
|
796
|
+
workflow: workflow,
|
|
797
|
+
message: describeError(err)
|
|
798
|
+
});
|
|
799
|
+
}
|
|
800
|
+
return {
|
|
801
|
+
settled: settled,
|
|
802
|
+
skipped: args.workflows.filter(workflow => !stillOrphaned(workflow)),
|
|
803
|
+
failures: failures
|
|
804
|
+
};
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
function workflowTally(workflows) {
|
|
808
|
+
const counts = /* @__PURE__ */ new Map;
|
|
809
|
+
for (const workflow of workflows) counts.set(workflow.title, (counts.get(workflow.title) ?? 0) + 1);
|
|
810
|
+
return [ ...counts ].map(([title, count]) => count === 1 ? title : `${title} ×${count}`);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
function settleToast(outcome) {
|
|
814
|
+
const total = outcome.settled + outcome.skipped.length + outcome.failures.length;
|
|
815
|
+
if (outcome.skipped.length === 0 && outcome.failures.length === 0) return {
|
|
816
|
+
status: "success",
|
|
817
|
+
title: outcome.settled === 1 ? "Cleaned up 1 workflow" : `Cleaned up ${outcome.settled} workflows`
|
|
818
|
+
};
|
|
819
|
+
const lines = [ ...outcome.skipped.map(workflow => `${workflow.title}: its document exists again — left running`), ...outcome.failures.map(failure => `${failure.workflow.title}: ${failure.message}`) ];
|
|
820
|
+
return {
|
|
821
|
+
status: "warning",
|
|
822
|
+
title: `Cleaned up ${outcome.settled} of ${total} workflows`,
|
|
823
|
+
description: lines.join(`\n`)
|
|
824
|
+
};
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function recheckFailedToast(err) {
|
|
828
|
+
return {
|
|
829
|
+
status: "error",
|
|
830
|
+
title: "Nothing was cleaned up — the documents could not be re-checked",
|
|
831
|
+
description: describeError(err)
|
|
832
|
+
};
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function OrphanedWorkflowsNote({orphans: orphans}) {
|
|
836
|
+
const [confirming, setConfirming] = useState(!1), count = orphans.workflows.length;
|
|
837
|
+
if (count === 0) return null;
|
|
838
|
+
const label = count === 1 ? "1 workflow is attached to a deleted document — its tasks aren’t listed." : `${count} workflows are attached to deleted documents — their tasks aren’t listed.`;
|
|
839
|
+
/* @__PURE__ */
|
|
840
|
+
return jsxs(Fragment, {
|
|
841
|
+
children: [
|
|
842
|
+
/* @__PURE__ */ jsx(CautionNote, {
|
|
843
|
+
action: /* @__PURE__ */ jsx(Button, {
|
|
844
|
+
fontSize: 1,
|
|
845
|
+
mode: "ghost",
|
|
846
|
+
onClick: () => setConfirming(!0),
|
|
847
|
+
padding: 2,
|
|
848
|
+
text: "Clean up…"
|
|
849
|
+
}),
|
|
850
|
+
label: label
|
|
851
|
+
}), confirming ? /* @__PURE__ */ jsx(SettleOrphansDialog, {
|
|
852
|
+
onClose: () => setConfirming(!1),
|
|
853
|
+
workflows: orphans.workflows
|
|
854
|
+
}) : null ]
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function SettleOrphansDialog({workflows: workflows, onClose: onClose}) {
|
|
859
|
+
const {engine: engine} = useWorkflowContext(), client = useClient({
|
|
860
|
+
apiVersion: WORKFLOW_API_VERSION
|
|
861
|
+
}), toast = useClosableToast(), [pending, setPending] = useState(!1), confirm = async () => {
|
|
862
|
+
setPending(!0);
|
|
863
|
+
try {
|
|
864
|
+
const outcome = await settleOrphans({
|
|
865
|
+
workflows: workflows,
|
|
866
|
+
probeMissing: ids => missingDocIds(client, ids),
|
|
867
|
+
abort: args => engine.abortInstance(args)
|
|
868
|
+
});
|
|
869
|
+
toast.push(settleToast(outcome));
|
|
870
|
+
} catch (err) {
|
|
871
|
+
toast.push(recheckFailedToast(err));
|
|
872
|
+
}
|
|
873
|
+
onClose();
|
|
874
|
+
};
|
|
875
|
+
/* @__PURE__ */
|
|
876
|
+
return jsx(Dialog, {
|
|
877
|
+
footer: /* @__PURE__ */ jsx(Box, {
|
|
878
|
+
padding: 3,
|
|
879
|
+
children: /* @__PURE__ */ jsxs(Flex, {
|
|
880
|
+
gap: 2,
|
|
881
|
+
justify: "flex-end",
|
|
882
|
+
children: [
|
|
883
|
+
/* @__PURE__ */ jsx(Button, {
|
|
884
|
+
disabled: pending,
|
|
885
|
+
fontSize: 1,
|
|
886
|
+
mode: "bleed",
|
|
887
|
+
onClick: onClose,
|
|
888
|
+
padding: 2,
|
|
889
|
+
text: "Cancel"
|
|
890
|
+
}),
|
|
891
|
+
/* @__PURE__ */ jsx(Button, {
|
|
892
|
+
fontSize: 1,
|
|
893
|
+
loading: pending,
|
|
894
|
+
onClick: () => {
|
|
895
|
+
confirm();
|
|
896
|
+
},
|
|
897
|
+
padding: 2,
|
|
898
|
+
text: workflows.length === 1 ? "Clean up 1 workflow" : `Clean up ${workflows.length} workflows`
|
|
899
|
+
}) ]
|
|
900
|
+
})
|
|
901
|
+
}),
|
|
902
|
+
header: "Clean up workflows",
|
|
903
|
+
id: "settle-orphaned-workflows",
|
|
904
|
+
onClose: onClose,
|
|
905
|
+
width: 0,
|
|
906
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
907
|
+
padding: 4,
|
|
908
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
909
|
+
gap: 4,
|
|
910
|
+
children: [
|
|
911
|
+
/* @__PURE__ */ jsx(Text, {
|
|
912
|
+
muted: !0,
|
|
913
|
+
size: 1,
|
|
914
|
+
children: "The following workflows exist for documents which have since been deleted:"
|
|
915
|
+
}),
|
|
916
|
+
/* @__PURE__ */ jsx(Stack, {
|
|
917
|
+
as: "ul",
|
|
918
|
+
gap: 2,
|
|
919
|
+
style: {
|
|
920
|
+
listStyle: "disc",
|
|
921
|
+
margin: 0,
|
|
922
|
+
paddingLeft: "1.25em"
|
|
923
|
+
},
|
|
924
|
+
children: workflowTally(workflows).map(line => /* @__PURE__ */ jsx(Text, {
|
|
925
|
+
as: "li",
|
|
926
|
+
size: 1,
|
|
927
|
+
style: {
|
|
928
|
+
display: "list-item"
|
|
929
|
+
},
|
|
930
|
+
weight: "medium",
|
|
931
|
+
children: line
|
|
932
|
+
}, line))
|
|
933
|
+
}),
|
|
934
|
+
/* @__PURE__ */ jsx(Text, {
|
|
935
|
+
muted: !0,
|
|
936
|
+
size: 1,
|
|
937
|
+
children: "Cleaning up these workflows will close them."
|
|
938
|
+
}) ]
|
|
939
|
+
})
|
|
940
|
+
})
|
|
941
|
+
});
|
|
942
|
+
}
|
|
943
|
+
|
|
592
944
|
function taskFilterOptions(groups) {
|
|
593
945
|
const workflows = /* @__PURE__ */ new Map, documents = /* @__PURE__ */ new Map;
|
|
594
946
|
for (const group of groups.withDocuments) {
|
|
@@ -605,8 +957,33 @@ function taskFilterOptions(groups) {
|
|
|
605
957
|
};
|
|
606
958
|
}
|
|
607
959
|
|
|
960
|
+
function activeEntries(filters) {
|
|
961
|
+
return Object.entries(filters).filter(([, value]) => value !== void 0 && (!Array.isArray(value) || value.length > 0));
|
|
962
|
+
}
|
|
963
|
+
|
|
608
964
|
function hasActiveFilters(filters) {
|
|
609
|
-
return
|
|
965
|
+
return activeEntries(filters).length > 0;
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
function sameFilters(a, b) {
|
|
969
|
+
return sameValueSet(a.statuses, b.statuses) && sameValueSet(a.workflows, b.workflows) && sameValueSet(a.documentIds, b.documentIds) && sameValueSet(a.assigneeIds, b.assigneeIds) && sameDate(a.date, b.date);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function sameValueSet(a, b) {
|
|
973
|
+
const av = a ?? [], bv = b ?? [];
|
|
974
|
+
return av.length === bv.length && av.every(value => bv.includes(value));
|
|
975
|
+
}
|
|
976
|
+
|
|
977
|
+
function sameDate(a, b) {
|
|
978
|
+
return typeof a == "object" && typeof b == "object" ? a.before === b.before : a === b;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
function taskFilterTelemetry(filters) {
|
|
982
|
+
const entries = activeEntries(filters);
|
|
983
|
+
return {
|
|
984
|
+
filterKinds: entries.map(([key]) => key).sort(),
|
|
985
|
+
filterCount: entries.reduce((n, [, value]) => n + (Array.isArray(value) ? value.length : 1), 0)
|
|
986
|
+
};
|
|
610
987
|
}
|
|
611
988
|
|
|
612
989
|
function toggledValue(args) {
|
|
@@ -761,7 +1138,12 @@ function useDocPreviewTitles(documents) {
|
|
|
761
1138
|
const HITS_CAP = 6;
|
|
762
1139
|
|
|
763
1140
|
function TaskFilterMenu({filters: filters, options: options, onChange: onChange}) {
|
|
764
|
-
const [open, setOpen] = useState(!1),
|
|
1141
|
+
const [open, setOpen] = useState(!1), telemetry = useWorkflowTelemetry(), openSnapshot = useRef(void 0), openMenu = () => {
|
|
1142
|
+
openSnapshot.current = filters, setOpen(!0);
|
|
1143
|
+
}, close = () => {
|
|
1144
|
+
setOpen(!1), openSnapshot.current !== void 0 && !sameFilters(openSnapshot.current, filters) && telemetry.log(WorkflowTaskFiltersApplied, taskFilterTelemetry(filters)),
|
|
1145
|
+
openSnapshot.current = void 0;
|
|
1146
|
+
}, active = hasActiveFilters(filters);
|
|
765
1147
|
/* @__PURE__ */
|
|
766
1148
|
return jsx(DismissablePopover, {
|
|
767
1149
|
content: open ? /* @__PURE__ */ jsx(FilterPanel, {
|
|
@@ -769,7 +1151,7 @@ function TaskFilterMenu({filters: filters, options: options, onChange: onChange}
|
|
|
769
1151
|
onChange: onChange,
|
|
770
1152
|
options: options
|
|
771
1153
|
}) : null,
|
|
772
|
-
onDismiss:
|
|
1154
|
+
onDismiss: close,
|
|
773
1155
|
open: open,
|
|
774
1156
|
overflow: "visible",
|
|
775
1157
|
children: /* @__PURE__ */ jsx(HoverHint, {
|
|
@@ -780,7 +1162,7 @@ function TaskFilterMenu({filters: filters, options: options, onChange: onChange}
|
|
|
780
1162
|
fontSize: 1,
|
|
781
1163
|
icon: FilterIcon,
|
|
782
1164
|
mode: "bleed",
|
|
783
|
-
onClick: () =>
|
|
1165
|
+
onClick: () => open ? close() : openMenu(),
|
|
784
1166
|
padding: 2,
|
|
785
1167
|
selected: active || open
|
|
786
1168
|
})
|
|
@@ -1122,7 +1504,7 @@ function DocumentField({documents: documents, activeIds: activeIds, onToggle: on
|
|
|
1122
1504
|
if (documents.length === 0) return null;
|
|
1123
1505
|
const rows = documents.map(doc => ({
|
|
1124
1506
|
key: doc.id,
|
|
1125
|
-
text: `${titles.get(doc.id) ?? ""} ${doc.type} ${
|
|
1507
|
+
text: `${titles.get(doc.id) ?? ""} ${doc.type} ${toBareId(doc.id)}`,
|
|
1126
1508
|
node: /* @__PURE__ */ jsx(DocRefFace, {
|
|
1127
1509
|
gdr: doc
|
|
1128
1510
|
})
|
|
@@ -1207,7 +1589,7 @@ function useDocumentChipLabels(documentIds, documents) {
|
|
|
1207
1589
|
const schema = useSchema(), activeDocs = documents.filter(doc => documentIds.includes(doc.id)), {titles: titles, resolved: resolved} = useDocPreviewTitles(activeDocs), labels = /* @__PURE__ */ new Map;
|
|
1208
1590
|
for (const doc of activeDocs) {
|
|
1209
1591
|
if (!resolved.has(doc.id)) continue;
|
|
1210
|
-
const label = titles.get(doc.id) ?? schemaTypeTitle(schema, doc.type) ??
|
|
1592
|
+
const label = titles.get(doc.id) ?? schemaTypeTitle(schema, doc.type) ?? toBareId(doc.id);
|
|
1211
1593
|
labels.set(doc.id, label);
|
|
1212
1594
|
}
|
|
1213
1595
|
return labels;
|
|
@@ -1276,7 +1658,7 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
|
|
|
1276
1658
|
});
|
|
1277
1659
|
const kind = dateControlKind(state), commit = (mode, value) => {
|
|
1278
1660
|
state.kind === "editable" && save(() => editFieldFor(instanceId, {
|
|
1279
|
-
target:
|
|
1661
|
+
target: editFieldTarget(state.field),
|
|
1280
1662
|
...mode === "set" ? {
|
|
1281
1663
|
mode: mode,
|
|
1282
1664
|
value: value
|
|
@@ -1334,6 +1716,28 @@ function ActivityDateControl({instanceId: instanceId, state: state, dates: dates
|
|
|
1334
1716
|
});
|
|
1335
1717
|
}
|
|
1336
1718
|
|
|
1719
|
+
const PREWARM_MARGIN = "600px 0px";
|
|
1720
|
+
|
|
1721
|
+
function useNearViewport() {
|
|
1722
|
+
const [element, setElement] = useState(null), [near, setNear] = useState(!1);
|
|
1723
|
+
return useEffect(() => {
|
|
1724
|
+
if (element === null) return;
|
|
1725
|
+
if (typeof IntersectionObserver > "u") {
|
|
1726
|
+
setNear(!0);
|
|
1727
|
+
return;
|
|
1728
|
+
}
|
|
1729
|
+
const observer = new IntersectionObserver(entries => {
|
|
1730
|
+
setNear(entries.at(-1)?.isIntersecting ?? !1);
|
|
1731
|
+
}, {
|
|
1732
|
+
rootMargin: PREWARM_MARGIN
|
|
1733
|
+
});
|
|
1734
|
+
return observer.observe(element), () => observer.disconnect();
|
|
1735
|
+
}, [ element ]), {
|
|
1736
|
+
ref: setElement,
|
|
1737
|
+
near: near
|
|
1738
|
+
};
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1337
1741
|
function RefChips({refs: refs, max: max = 2}) {
|
|
1338
1742
|
if (refs.length === 0) return null;
|
|
1339
1743
|
const shown = refs.slice(0, max), overflow = refs.length - shown.length;
|
|
@@ -1355,15 +1759,17 @@ function RefChips({refs: refs, max: max = 2}) {
|
|
|
1355
1759
|
|
|
1356
1760
|
const EMPTY_NOTE = "No tasks match — adjust the filters, or start a workflow", UNGROUPED_TITLE = "Workflows without Studio documents";
|
|
1357
1761
|
|
|
1358
|
-
function TaskGroupList({groups: groups, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1762
|
+
function TaskGroupList({bands: bands, groups: groups, instanceDefaultOpen: instanceDefaultOpen, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1359
1763
|
return groups.withDocuments.length === 0 && groups.withoutDocuments.length === 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1360
1764
|
text: EMPTY_NOTE
|
|
1361
1765
|
}) : /* @__PURE__ */ jsxs(Stack, {
|
|
1362
1766
|
gap: 2,
|
|
1363
1767
|
children: [ groups.withDocuments.map(group => /* @__PURE__ */ jsx(CollapsibleBand, {
|
|
1364
1768
|
background: !0,
|
|
1769
|
+
onToggle: () => bands.toggle(group.key, GROUP_DEFAULT_OPEN),
|
|
1770
|
+
open: bands.isOpen(group.key, GROUP_DEFAULT_OPEN),
|
|
1365
1771
|
sticky: !0,
|
|
1366
|
-
title:
|
|
1772
|
+
title: toBareId(group.document.id),
|
|
1367
1773
|
header: /* @__PURE__ */ jsx(Flex, {
|
|
1368
1774
|
align: "center",
|
|
1369
1775
|
flex: "none",
|
|
@@ -1376,6 +1782,8 @@ function TaskGroupList({groups: groups, onOpenTask: onOpenTask, onOpenWorkflow:
|
|
|
1376
1782
|
})
|
|
1377
1783
|
}),
|
|
1378
1784
|
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
1785
|
+
bands: bands,
|
|
1786
|
+
instanceDefaultOpen: instanceDefaultOpen,
|
|
1379
1787
|
instances: group.instances,
|
|
1380
1788
|
onOpenTask: onOpenTask,
|
|
1381
1789
|
onOpenWorkflow: onOpenWorkflow,
|
|
@@ -1383,6 +1791,8 @@ function TaskGroupList({groups: groups, onOpenTask: onOpenTask, onOpenWorkflow:
|
|
|
1383
1791
|
})
|
|
1384
1792
|
}, group.key)), groups.withoutDocuments.length > 0 ? /* @__PURE__ */ jsx(CollapsibleBand, {
|
|
1385
1793
|
background: !0,
|
|
1794
|
+
onToggle: () => bands.toggle(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
|
|
1795
|
+
open: bands.isOpen(UNGROUPED_BAND_KEY, GROUP_DEFAULT_OPEN),
|
|
1386
1796
|
sticky: !0,
|
|
1387
1797
|
title: UNGROUPED_TITLE,
|
|
1388
1798
|
header: /* @__PURE__ */ jsx(Text, {
|
|
@@ -1391,6 +1801,8 @@ function TaskGroupList({groups: groups, onOpenTask: onOpenTask, onOpenWorkflow:
|
|
|
1391
1801
|
children: UNGROUPED_TITLE
|
|
1392
1802
|
}),
|
|
1393
1803
|
children: /* @__PURE__ */ jsx(InstanceBands, {
|
|
1804
|
+
bands: bands,
|
|
1805
|
+
instanceDefaultOpen: instanceDefaultOpen,
|
|
1394
1806
|
instances: groups.withoutDocuments,
|
|
1395
1807
|
onOpenTask: onOpenTask,
|
|
1396
1808
|
onOpenWorkflow: onOpenWorkflow,
|
|
@@ -1408,12 +1820,14 @@ function targetOf(row) {
|
|
|
1408
1820
|
};
|
|
1409
1821
|
}
|
|
1410
1822
|
|
|
1411
|
-
function InstanceBands({instances: instances, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1823
|
+
function InstanceBands({bands: bands, instanceDefaultOpen: instanceDefaultOpen, instances: instances, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1412
1824
|
/* @__PURE__ */
|
|
1413
1825
|
return jsx(Stack, {
|
|
1414
1826
|
gap: 1,
|
|
1415
1827
|
children: instances.map(band => /* @__PURE__ */ jsx(InstanceBand, {
|
|
1416
1828
|
band: band,
|
|
1829
|
+
bands: bands,
|
|
1830
|
+
instanceDefaultOpen: instanceDefaultOpen,
|
|
1417
1831
|
onOpenTask: onOpenTask,
|
|
1418
1832
|
onOpenWorkflow: onOpenWorkflow,
|
|
1419
1833
|
showTerminalActions: showTerminalActions
|
|
@@ -1421,10 +1835,12 @@ function InstanceBands({instances: instances, onOpenTask: onOpenTask, onOpenWork
|
|
|
1421
1835
|
});
|
|
1422
1836
|
}
|
|
1423
1837
|
|
|
1424
|
-
function InstanceBand({band: band, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1838
|
+
function InstanceBand({band: band, bands: bands, instanceDefaultOpen: instanceDefaultOpen, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, showTerminalActions: showTerminalActions}) {
|
|
1425
1839
|
const {resolvePathFromState: resolvePathFromState} = useRouter();
|
|
1426
1840
|
/* @__PURE__ */
|
|
1427
1841
|
return jsx(CollapsibleBand, {
|
|
1842
|
+
onToggle: () => bands.toggle(band.instanceId, instanceDefaultOpen),
|
|
1843
|
+
open: bands.isOpen(band.instanceId, instanceDefaultOpen),
|
|
1428
1844
|
title: band.title,
|
|
1429
1845
|
header: /* @__PURE__ */ jsxs(Fragment, {
|
|
1430
1846
|
children: [
|
|
@@ -1486,26 +1902,86 @@ function TaskRows({rows: rows, onOpenTask: onOpenTask, showTerminalActions: show
|
|
|
1486
1902
|
}
|
|
1487
1903
|
|
|
1488
1904
|
function TaskActivityRow({row: row, onOpen: onOpen, showTerminalActions: showTerminalActions}) {
|
|
1489
|
-
const
|
|
1905
|
+
const {ref: ref, near: near} = useNearViewport(), entry = useWorkflowInstanceEntry(row.instanceId, {
|
|
1906
|
+
evaluate: near
|
|
1907
|
+
}), render = taskRowRender(row, entry);
|
|
1490
1908
|
/* @__PURE__ */
|
|
1491
|
-
return jsx(
|
|
1492
|
-
|
|
1493
|
-
children: /* @__PURE__ */ jsx(
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1909
|
+
return jsx(Box, {
|
|
1910
|
+
ref: ref,
|
|
1911
|
+
children: /* @__PURE__ */ jsx(StaleLock, {
|
|
1912
|
+
stale: render.stale,
|
|
1913
|
+
children: /* @__PURE__ */ jsx(ActivityRow, {
|
|
1914
|
+
assignState: render.assignState,
|
|
1915
|
+
assigneeIds: render.assigneeIds,
|
|
1916
|
+
dateControl: /* @__PURE__ */ jsx(ActivityDateControl, {
|
|
1917
|
+
dates: render.dates,
|
|
1918
|
+
instanceId: row.instanceId,
|
|
1919
|
+
state: render.dateState
|
|
1920
|
+
}),
|
|
1921
|
+
face: render.face,
|
|
1498
1922
|
instanceId: row.instanceId,
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
instanceId: row.instanceId,
|
|
1503
|
-
onOpen: onOpen,
|
|
1504
|
-
terminalActions: showTerminalActions ? render.terminalActions : []
|
|
1923
|
+
onOpen: onOpen,
|
|
1924
|
+
terminalActions: showTerminalActions ? render.terminalActions : []
|
|
1925
|
+
})
|
|
1505
1926
|
})
|
|
1506
1927
|
});
|
|
1507
1928
|
}
|
|
1508
1929
|
|
|
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
|
+
const NO_MISSING = /* @__PURE__ */ new Set;
|
|
1954
|
+
|
|
1955
|
+
function sameIdSet(a, b) {
|
|
1956
|
+
if (a.size !== b.size) return !1;
|
|
1957
|
+
for (const id of a) if (!b.has(id)) return !1;
|
|
1958
|
+
return !0;
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
function useOrphanedWorkflows(groups) {
|
|
1962
|
+
const {actualTypes: actualTypes, binding: binding} = useWorkflowContext(), contentResource = binding.contentResource, key = useMemo(() => orphanProbeIds(groups, contentResource), [ groups, contentResource ]).join("|"), [missing, setMissing] = useState(NO_MISSING);
|
|
1963
|
+
return useEffect(() => {
|
|
1964
|
+
if (key === "") {
|
|
1965
|
+
setMissing(NO_MISSING);
|
|
1966
|
+
return;
|
|
1967
|
+
}
|
|
1968
|
+
const tracked = key.split("|"), releases = tracked.map(id => actualTypes.track(id)), recompute = () => {
|
|
1969
|
+
const next = new Set(tracked.filter(id => actualTypes.read(id) === null));
|
|
1970
|
+
setMissing(current => sameIdSet(current, next) ? current : next);
|
|
1971
|
+
};
|
|
1972
|
+
recompute();
|
|
1973
|
+
const unsubscribe = actualTypes.subscribe(recompute);
|
|
1974
|
+
return () => {
|
|
1975
|
+
unsubscribe();
|
|
1976
|
+
for (const release of releases) release();
|
|
1977
|
+
};
|
|
1978
|
+
}, [ actualTypes, key ]), useMemo(() => missing.size === 0 ? NO_ORPHANS : orphanVerdict({
|
|
1979
|
+
groups: groups,
|
|
1980
|
+
contentResource: contentResource,
|
|
1981
|
+
missing: missing
|
|
1982
|
+
}), [ groups, contentResource, missing ]);
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1509
1985
|
const INSTANCE_CAP = 200, NEWEST_INSTANCES = {
|
|
1510
1986
|
includeCompleted: !0,
|
|
1511
1987
|
limit: INSTANCE_CAP
|
|
@@ -1528,18 +2004,21 @@ function useToolInstances() {
|
|
|
1528
2004
|
}, [ instances, loading, unreadable ]);
|
|
1529
2005
|
}
|
|
1530
2006
|
|
|
1531
|
-
function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity,
|
|
1532
|
-
const
|
|
2007
|
+
function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, segment: segment, truncated: truncated, unreadable: unreadable}) {
|
|
2008
|
+
const mineOnly = segment === "for-me", {binding: binding} = useWorkflowContext(), bands = useBandOpenState({
|
|
2009
|
+
segment: segment,
|
|
2010
|
+
scope: binding.engineResource.id
|
|
2011
|
+
}), [filters, setFilters] = useState({}), groups = useMemo(() => deriveTaskGroups({
|
|
1533
2012
|
instances: instances,
|
|
1534
2013
|
identity: identity
|
|
1535
|
-
}), [ instances, identity ]),
|
|
2014
|
+
}), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = useMemo(() => taskFilterOptions(listed), [ listed ]), visible = useMemo(() => {
|
|
1536
2015
|
const filtered = applyTaskFilters({
|
|
1537
|
-
groups:
|
|
2016
|
+
groups: listed,
|
|
1538
2017
|
filters: filters,
|
|
1539
2018
|
now: /* @__PURE__ */ new Date
|
|
1540
2019
|
});
|
|
1541
2020
|
return mineOnly ? onlyMyTasks(filtered) : filtered;
|
|
1542
|
-
}, [
|
|
2021
|
+
}, [ listed, filters, mineOnly ]);
|
|
1543
2022
|
return loading ?
|
|
1544
2023
|
/* @__PURE__ */ jsx(Box, {
|
|
1545
2024
|
paddingLeft: 2,
|
|
@@ -1560,8 +2039,13 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
1560
2039
|
/* @__PURE__ */ jsx(UnreadableDocsNote, {
|
|
1561
2040
|
unreadable: unreadable
|
|
1562
2041
|
}),
|
|
2042
|
+
/* @__PURE__ */ jsx(OrphanedWorkflowsNote, {
|
|
2043
|
+
orphans: orphans
|
|
2044
|
+
}),
|
|
1563
2045
|
/* @__PURE__ */ jsx(TaskGroupList, {
|
|
2046
|
+
bands: bands,
|
|
1564
2047
|
groups: visible,
|
|
2048
|
+
instanceDefaultOpen: instanceBandDefaultOpen(segment),
|
|
1565
2049
|
onOpenTask: onOpenTask,
|
|
1566
2050
|
onOpenWorkflow: onOpenWorkflow,
|
|
1567
2051
|
showTerminalActions: mineOnly
|
|
@@ -1602,7 +2086,12 @@ function carryingSearchParams(state, searchParams) {
|
|
|
1602
2086
|
}
|
|
1603
2087
|
|
|
1604
2088
|
function WorkflowsToolRoot() {
|
|
1605
|
-
const router = useRouter(), {seedInstance: seedInstance} = useWorkflowContext(),
|
|
2089
|
+
const router = useRouter(), {seedInstance: seedInstance} = useWorkflowContext(), {tab: tab} = useToolTab();
|
|
2090
|
+
useLogEventOnMount(WorkflowToolOpened, {
|
|
2091
|
+
tab: tab,
|
|
2092
|
+
via: "open"
|
|
2093
|
+
});
|
|
2094
|
+
const instanceId = typeof router.state.instanceId == "string" ? router.state.instanceId : void 0;
|
|
1606
2095
|
return instanceId ? /* @__PURE__ */ jsx(WorkflowInstanceDetail, {
|
|
1607
2096
|
instanceId: instanceId,
|
|
1608
2097
|
onBack: () => router.navigate(carryingSearchParams({}, router.state._searchParams))
|
|
@@ -1616,7 +2105,7 @@ function WorkflowsToolRoot() {
|
|
|
1616
2105
|
}
|
|
1617
2106
|
|
|
1618
2107
|
function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
1619
|
-
const {tab: tab, setTab: setTab} = useToolTab(), instances = useToolInstances(), identity = useAssignmentIdentity(), [openTask, setOpenTask] = useState(null), openInstance = target => {
|
|
2108
|
+
const {tab: tab, setTab: setTab} = useToolTab(), telemetry = useWorkflowTelemetry(), instances = useToolInstances(), identity = useAssignmentIdentity(), [openTask, setOpenTask] = useState(null), openInstance = target => {
|
|
1620
2109
|
if (typeof target != "string") return onOpenInstance(target);
|
|
1621
2110
|
const held = [ ...instances.inFlight, ...instances.settled ].find(i => i._id === target);
|
|
1622
2111
|
onOpenInstance(held ?? target);
|
|
@@ -1660,7 +2149,12 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
|
1660
2149
|
/* @__PURE__ */ jsx(TabSwitch, {
|
|
1661
2150
|
ariaControls: "workflows-tool-panel",
|
|
1662
2151
|
idPrefix: "workflows-tool-tab",
|
|
1663
|
-
onSelect:
|
|
2152
|
+
onSelect: next => {
|
|
2153
|
+
next !== tab && telemetry.log(WorkflowToolOpened, {
|
|
2154
|
+
tab: next,
|
|
2155
|
+
via: "tab-switch"
|
|
2156
|
+
}), setTab(next);
|
|
2157
|
+
},
|
|
1664
2158
|
options: TOOL_TABS,
|
|
1665
2159
|
selected: tab
|
|
1666
2160
|
}),
|
|
@@ -1691,9 +2185,9 @@ function WorkflowsHome({onOpenInstance: onOpenInstance}) {
|
|
|
1691
2185
|
identity: identity,
|
|
1692
2186
|
instances: [ ...instances.inFlight, ...instances.settled ],
|
|
1693
2187
|
loading: instances.loading,
|
|
1694
|
-
mineOnly: tab === "for-me",
|
|
1695
2188
|
onOpenTask: setOpenTask,
|
|
1696
2189
|
onOpenWorkflow: openInstance,
|
|
2190
|
+
segment: tab,
|
|
1697
2191
|
truncated: instances.truncated,
|
|
1698
2192
|
unreadable: instances.unreadable
|
|
1699
2193
|
})
|