@sanity/workflow-studio-plugin 0.27.0 → 0.29.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 +427 -0
- package/dist/_chunks-cjs/index.cjs +650 -221
- package/dist/_chunks-cjs/workflows-tool-root.cjs +820 -312
- package/dist/_chunks-es/index.js +630 -213
- package/dist/_chunks-es/workflows-tool-root.js +834 -316
- package/package.json +12 -12
|
@@ -8,22 +8,28 @@ import { useState, useEffect, useMemo, useRef, useSyncExternalStore, useCallback
|
|
|
8
8
|
|
|
9
9
|
import { useRouter } from "sanity/router";
|
|
10
10
|
|
|
11
|
-
import { isLiveEntry, AbortWorkflowDialog,
|
|
11
|
+
import { isLiveEntry, AbortWorkflowDialog, useSpaceToken, isEvaluationStale, DocPreviewLink, SpinnerSlot, useWorkflowInstanceEntry, useLogEventOnMount, WorkflowInstanceDetailViewed, LoadingRow, InvalidDocNotice, useDefinition, useContainerToken, openActivityGone, InstanceFaultNote, InstanceSnapshotBody, ActivityLog, ActivityDetailDialog, instanceBreadcrumb, CodeChip, instanceTitle, dueDatesOf, isDueDatePast, isOpenActivityStatus, definitionSnapshotOf, stageTitle, isActivityAssignedTo, namesNoDeployedDefinition, mappingIssueDetail, formatTimeAgo, formatShortDateTime, useWorkflowContext, readDeployedDefinitions, describeError, gdrLocality, openableSchemaType, identityOf, useMinuteClock, NoteBanner, EmptyState, CountedHeading, AlertGlyph, MetricPair, formatShortAgo, InlineEmphasis, DismissablePopover, createSubscribers, findActivity, stateByActivity, rowDateControlState, rowAssignControlState, isRoleAssignedTo, assigneesOf, datesOf, committedRowFace, activityRowProps, TOAST_ID, WORKFLOW_API_VERSION, useWorkflowToast, formatDate, parseStoredDateValue, useUserDisplays, useProjectMembers, HoverHint, WorkflowTaskFiltersApplied, DocRefFace, Hairline, LinkChip, BreadcrumbTail, StageFace, StaleLock, ActivityRow, WorkflowTitleSeedDrifted, UnreadableDocsNote, RoleAssignedNotice, ForMeEmptyState, MetaRow, TabSwitch, WORKFLOW_PAGE_TABS, definitionFingerprint, LogEventOnMount, WorkflowBoardWorkflowSelected, WorkflowDefinitionDetailViewed, toolRoute, landingState, TOOL_TABS, tabSelectionNavigates, WorkflowToolOpened, toolTabState, workflowPageState, instanceState, backToTabLabel, useAssignmentIdentity, workflowState } from "./index.js";
|
|
12
12
|
|
|
13
|
-
import { entryDocRefs, startKindOf, isSubjectEntry, isInputSourced,
|
|
13
|
+
import { entryDocRefs, toBareId, findOpenStageEntry, terminalState, documentStuckCause, startKindOf, isSubjectEntry, isInputSourced, errorMessage, latestDeployedDefinitions, isStartableDefinition, findActivityNode } from "@sanity/workflow-engine";
|
|
14
14
|
|
|
15
15
|
import { ArrowLeftIcon } from "@sanity/icons/ArrowLeft";
|
|
16
16
|
|
|
17
|
-
import {
|
|
17
|
+
import { CalendarIcon } from "@sanity/icons/Calendar";
|
|
18
18
|
|
|
19
|
-
import {
|
|
19
|
+
import { ClockIcon } from "@sanity/icons/Clock";
|
|
20
20
|
|
|
21
|
-
import
|
|
21
|
+
import { DocumentIcon } from "@sanity/icons/Document";
|
|
22
22
|
|
|
23
|
-
import {
|
|
23
|
+
import { ErrorOutlineIcon } from "@sanity/icons/ErrorOutline";
|
|
24
|
+
|
|
25
|
+
import { TransferIcon } from "@sanity/icons/Transfer";
|
|
26
|
+
|
|
27
|
+
import { useDocumentPreviewStore, useSchema, usePerspective, useClient } from "sanity";
|
|
24
28
|
|
|
25
29
|
import { useWorkflowInstances } from "@sanity/workflow-studio";
|
|
26
30
|
|
|
31
|
+
import pluralize from "pluralize-esm";
|
|
32
|
+
|
|
27
33
|
import { AddIcon } from "@sanity/icons/Add";
|
|
28
34
|
|
|
29
35
|
import { SearchIcon } from "@sanity/icons/Search";
|
|
@@ -46,6 +52,10 @@ import { ChevronRightIcon } from "@sanity/icons/ChevronRight";
|
|
|
46
52
|
|
|
47
53
|
import { isReferenceSchemaType } from "@sanity/types";
|
|
48
54
|
|
|
55
|
+
import { EmptyIcon } from "@sanity/icons/Empty";
|
|
56
|
+
|
|
57
|
+
import { WarningOutlineIcon } from "@sanity/icons/WarningOutline";
|
|
58
|
+
|
|
49
59
|
import { WorkflowDiagram } from "@sanity/workflow-diagram";
|
|
50
60
|
|
|
51
61
|
import { useReducedMotion, motion, useSpring, useMotionValueEvent } from "motion/react";
|
|
@@ -89,11 +99,22 @@ function documentRefsOf(instance) {
|
|
|
89
99
|
return [ ...seen.values() ];
|
|
90
100
|
}
|
|
91
101
|
|
|
102
|
+
function rootInstanceOf(instance, byId) {
|
|
103
|
+
const rootRef = instance.ancestors[0];
|
|
104
|
+
return rootRef ? byId.get(toBareId(rootRef.id)) ?? instance : instance;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const TOOL_PANEL_PADDING = 3, TOOL_HEADING_PADDING = 2, PANEL_STEP = 3;
|
|
108
|
+
|
|
92
109
|
function TabStatusRow({children: children}) {
|
|
110
|
+
const firstLineOffset = useSpaceToken(PANEL_STEP) + useSpaceToken(TOOL_HEADING_PADDING);
|
|
93
111
|
/* @__PURE__ */
|
|
94
112
|
return jsx(Box, {
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
paddingBottom: PANEL_STEP,
|
|
114
|
+
paddingLeft: TOOL_HEADING_PADDING,
|
|
115
|
+
style: {
|
|
116
|
+
paddingTop: firstLineOffset
|
|
117
|
+
},
|
|
97
118
|
children: children
|
|
98
119
|
});
|
|
99
120
|
}
|
|
@@ -344,14 +365,24 @@ function InstanceDetailBody({entry: entry, framed: framed, subject: subject}) {
|
|
|
344
365
|
},
|
|
345
366
|
children: /* @__PURE__ */ jsxs(Stack, {
|
|
346
367
|
gap: 5,
|
|
347
|
-
children: [
|
|
368
|
+
children: [
|
|
369
|
+
/* @__PURE__ */ jsx(InstanceFaultNote, {
|
|
370
|
+
entry: entry,
|
|
371
|
+
scope: {
|
|
372
|
+
kind: "run",
|
|
373
|
+
onOpenActivity: activityName => setOpenActivity({
|
|
374
|
+
stage: instance.currentStage,
|
|
375
|
+
activityName: activityName
|
|
376
|
+
})
|
|
377
|
+
}
|
|
378
|
+
}), subject === "preview" ? /* @__PURE__ */ jsx(SubjectPreview, {
|
|
348
379
|
instance: instance
|
|
349
380
|
}) : null,
|
|
350
381
|
/* @__PURE__ */ jsxs(Stack, {
|
|
351
382
|
gap: 4,
|
|
352
383
|
children: [
|
|
353
384
|
/* @__PURE__ */ jsx(SectionHeading, {
|
|
354
|
-
children: "Stages
|
|
385
|
+
children: "Stages"
|
|
355
386
|
}),
|
|
356
387
|
/* @__PURE__ */ jsx(InstanceSnapshotBody, {
|
|
357
388
|
entry: entry,
|
|
@@ -433,12 +464,140 @@ function byResolvedTitle(titleOf, keyOf) {
|
|
|
433
464
|
};
|
|
434
465
|
}
|
|
435
466
|
|
|
467
|
+
function openActivitiesOf(instance) {
|
|
468
|
+
return (findOpenStageEntry(instance)?.activities ?? []).filter(activity => isOpenActivityStatus(activity.status));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function overdueTaskCount(instance, now) {
|
|
472
|
+
return openActivitiesOf(instance).filter(activity => dueDatesOf(activity.fields ?? []).some(due => isDueDatePast(due, now))).length;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const NO_SIGNALS = {
|
|
476
|
+
activeCount: 0,
|
|
477
|
+
assignedToMe: 0,
|
|
478
|
+
documentCount: 0,
|
|
479
|
+
documentlessCount: 0,
|
|
480
|
+
initialStageDocumentCount: 0,
|
|
481
|
+
lastTransition: void 0,
|
|
482
|
+
overdueDocumentCount: 0,
|
|
483
|
+
overdueDocumentlessCount: 0,
|
|
484
|
+
stuckCount: 0
|
|
485
|
+
};
|
|
486
|
+
|
|
487
|
+
function noDefinitionSignals() {
|
|
488
|
+
return NO_SIGNALS;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
function holdsWorkFor(instance, who) {
|
|
492
|
+
return openActivitiesOf(instance).some(activity => isActivityAssignedTo({
|
|
493
|
+
state: activity.fields ?? [],
|
|
494
|
+
who: who
|
|
495
|
+
}));
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function arrivalOf(instance, byId) {
|
|
499
|
+
const stage = findOpenStageEntry(instance);
|
|
500
|
+
if (stage !== void 0) return {
|
|
501
|
+
at: stage.enteredAt,
|
|
502
|
+
document: documentRefOf(instance, byId),
|
|
503
|
+
stage: stageTitle(definitionSnapshotOf(instance), stage.name)
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
function laterArrival(held, next) {
|
|
508
|
+
return next === void 0 ? held : held === void 0 || next.at > held.at ? next : held;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
function documentRefOf(instance, byId) {
|
|
512
|
+
return documentRefsOf(rootInstanceOf(instance, byId))[0];
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const NO_DRAFT = {
|
|
516
|
+
...NO_SIGNALS,
|
|
517
|
+
documents: /* @__PURE__ */ new Set,
|
|
518
|
+
initialStageDocuments: /* @__PURE__ */ new Set,
|
|
519
|
+
overdueDocuments: /* @__PURE__ */ new Set
|
|
520
|
+
};
|
|
521
|
+
|
|
522
|
+
function withDocument(held, document2) {
|
|
523
|
+
return document2 === void 0 ? held : new Set(held).add(document2);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
function atInitialStage(instance) {
|
|
527
|
+
const snapshot = definitionSnapshotOf(instance);
|
|
528
|
+
return snapshot !== void 0 && instance.currentStage === snapshot.initialStage;
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function fold(args) {
|
|
532
|
+
const {byId: byId, held: held, instance: instance, now: now, who: who} = args, stuck = documentStuckCause({
|
|
533
|
+
instance: instance,
|
|
534
|
+
definition: definitionSnapshotOf(instance)
|
|
535
|
+
}), document2 = documentRefOf(instance, byId)?.id, overdue = overdueTaskCount(instance, now) > 0;
|
|
536
|
+
return {
|
|
537
|
+
activeCount: held.activeCount + 1,
|
|
538
|
+
assignedToMe: held.assignedToMe + (who !== void 0 && holdsWorkFor(instance, who) ? 1 : 0),
|
|
539
|
+
documentlessCount: held.documentlessCount + (document2 === void 0 ? 1 : 0),
|
|
540
|
+
documents: withDocument(held.documents, document2),
|
|
541
|
+
initialStageDocuments: atInitialStage(instance) ? withDocument(held.initialStageDocuments, document2) : held.initialStageDocuments,
|
|
542
|
+
lastTransition: laterArrival(held.lastTransition, arrivalOf(instance, byId)),
|
|
543
|
+
overdueDocumentlessCount: held.overdueDocumentlessCount + (overdue && document2 === void 0 ? 1 : 0),
|
|
544
|
+
overdueDocuments: overdue ? withDocument(held.overdueDocuments, document2) : held.overdueDocuments,
|
|
545
|
+
stuckCount: held.stuckCount + (stuck === void 0 ? 0 : 1)
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function settle(draft) {
|
|
550
|
+
const {documents: documents, initialStageDocuments: initialStageDocuments, overdueDocuments: overdueDocuments, ...rest} = draft;
|
|
551
|
+
return {
|
|
552
|
+
...rest,
|
|
553
|
+
documentCount: documents.size,
|
|
554
|
+
initialStageDocumentCount: initialStageDocuments.size,
|
|
555
|
+
overdueDocumentCount: overdueDocuments.size
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
function definitionSignals(args) {
|
|
560
|
+
const byId = new Map(args.instances.map(instance => [ instance._id, instance ])), drafts = /* @__PURE__ */ new Map;
|
|
561
|
+
for (const instance of args.instances) {
|
|
562
|
+
if (terminalState(instance) !== "in-flight") continue;
|
|
563
|
+
const held = drafts.get(instance.definition) ?? NO_DRAFT;
|
|
564
|
+
drafts.set(instance.definition, fold({
|
|
565
|
+
byId: byId,
|
|
566
|
+
held: held,
|
|
567
|
+
instance: instance,
|
|
568
|
+
now: args.now,
|
|
569
|
+
who: args.identity
|
|
570
|
+
}));
|
|
571
|
+
}
|
|
572
|
+
return new Map([ ...drafts ].map(([name, draft]) => [ name, settle(draft) ]));
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function catalogBands(rows) {
|
|
576
|
+
const assigned = rows.filter(row => row.signals.assignedToMe > 0);
|
|
577
|
+
if (assigned.length === 0) return [ {
|
|
578
|
+
rows: rows,
|
|
579
|
+
title: void 0
|
|
580
|
+
} ];
|
|
581
|
+
const rest = rows.filter(row => row.signals.assignedToMe === 0);
|
|
582
|
+
return [ {
|
|
583
|
+
rows: assigned,
|
|
584
|
+
title: "Assigned to you"
|
|
585
|
+
}, {
|
|
586
|
+
rows: rest,
|
|
587
|
+
title: "Other workflows"
|
|
588
|
+
} ].filter(band => band.rows.length > 0);
|
|
589
|
+
}
|
|
590
|
+
|
|
436
591
|
function definitionCatalog(args) {
|
|
437
|
-
const rejected = [ ...args.issues.values() ]
|
|
592
|
+
const rejected = [ ...args.issues.values() ], signals = definitionSignals({
|
|
593
|
+
identity: args.identity,
|
|
594
|
+
instances: args.instances,
|
|
595
|
+
now: args.now
|
|
596
|
+
});
|
|
438
597
|
return args.definitions.map(definition => ({
|
|
439
598
|
name: definition.name,
|
|
440
599
|
title: definition.title ?? definition.name,
|
|
441
|
-
|
|
600
|
+
signals: signals.get(definition.name) ?? noDefinitionSignals(),
|
|
442
601
|
docTypes: [ ...new Set(args.mappings.filter(mapping => mapping.definition === definition.name).map(mapping => mapping.docType)) ],
|
|
443
602
|
brokenBindings: rejected.filter(issue => issue.definition === definition.name && issue.version === definition.version).map(issue => ({
|
|
444
603
|
docType: issue.docType,
|
|
@@ -476,28 +635,60 @@ function deployedPhrase(definition) {
|
|
|
476
635
|
return ago === "" ? formatShortDateTime(deployedAt) : `${formatShortDateTime(deployedAt)} (${ago})`;
|
|
477
636
|
}
|
|
478
637
|
|
|
479
|
-
const
|
|
638
|
+
const logged = /* @__PURE__ */ new WeakSet;
|
|
480
639
|
|
|
481
|
-
function
|
|
482
|
-
|
|
640
|
+
function logReadFailure(what, error) {
|
|
641
|
+
if (typeof error == "object" && error !== null) {
|
|
642
|
+
if (logged.has(error)) return;
|
|
643
|
+
logged.add(error);
|
|
644
|
+
}
|
|
645
|
+
console.error(`[workflow-studio-plugin] could not load ${what}:`, error);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function useReadFailureLog(what, error) {
|
|
649
|
+
useEffect(() => {
|
|
650
|
+
error !== void 0 && logReadFailure(what, error);
|
|
651
|
+
}, [ what, error ]);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
const INSTANCE_CAP = 200, INSTANCES_READ_FAILED = "Couldn’t load workflow data. Try again later.";
|
|
655
|
+
|
|
656
|
+
function useCappedInstances(filter) {
|
|
657
|
+
const {engine: engine} = useWorkflowContext(), capped = useMemo(() => ({
|
|
658
|
+
...filter,
|
|
659
|
+
limit: INSTANCE_CAP
|
|
660
|
+
}), [ filter ]), {instances: instances, loading: loading, unreadable: unreadable, error: error} = useWorkflowInstances({
|
|
661
|
+
engine: engine,
|
|
662
|
+
filter: capped
|
|
663
|
+
});
|
|
664
|
+
return useReadFailureLog("workflow data", error), useMemo(() => {
|
|
665
|
+
const rows = instances ?? [];
|
|
666
|
+
return {
|
|
667
|
+
rows: rows,
|
|
668
|
+
truncated: rows.length + unreadable.length === INSTANCE_CAP,
|
|
669
|
+
loading: loading,
|
|
670
|
+
unreadable: unreadable,
|
|
671
|
+
error: error === void 0 ? void 0 : errorMessage(error)
|
|
672
|
+
};
|
|
673
|
+
}, [ instances, loading, unreadable, error ]);
|
|
483
674
|
}
|
|
484
675
|
|
|
485
|
-
const lastRead = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
|
|
676
|
+
const CATALOG_READ_FAILED = "Couldn’t load the deployed workflows. Try again later.", lastRead$1 = /* @__PURE__ */ new WeakMap, issuedReads = /* @__PURE__ */ new WeakMap;
|
|
486
677
|
|
|
487
678
|
function useDeployedDefinitions() {
|
|
488
|
-
const {engine: engine} = useWorkflowContext(), [definitions, setDefinitions] = useState(() => lastRead.get(engine)?.rows), [error, setError] = useState(void 0);
|
|
679
|
+
const {engine: engine} = useWorkflowContext(), [definitions, setDefinitions] = useState(() => lastRead$1.get(engine)?.rows), [error, setError] = useState(void 0);
|
|
489
680
|
return useEffect(() => {
|
|
490
681
|
let cancelled = !1;
|
|
491
|
-
setDefinitions(lastRead.get(engine)?.rows), setError(void 0);
|
|
682
|
+
setDefinitions(lastRead$1.get(engine)?.rows), setError(void 0);
|
|
492
683
|
const seq = (issuedReads.get(engine) ?? 0) + 1;
|
|
493
684
|
return issuedReads.set(engine, seq), (async () => {
|
|
494
|
-
const rows = await readDeployedDefinitions(engine), latest = latestDeployedDefinitions(rows), held = lastRead.get(engine);
|
|
495
|
-
(held === void 0 || seq > held.seq) && lastRead.set(engine, {
|
|
685
|
+
const rows = await readDeployedDefinitions(engine), latest = latestDeployedDefinitions(rows), held = lastRead$1.get(engine);
|
|
686
|
+
(held === void 0 || seq > held.seq) && lastRead$1.set(engine, {
|
|
496
687
|
seq: seq,
|
|
497
688
|
rows: latest
|
|
498
689
|
}), cancelled || (setDefinitions(latest), setError(void 0));
|
|
499
690
|
})().catch(err => {
|
|
500
|
-
cancelled || setError(describeError(err));
|
|
691
|
+
logReadFailure("the deployed workflows", err), cancelled || setError(describeError(err));
|
|
501
692
|
}), () => {
|
|
502
693
|
cancelled = !0;
|
|
503
694
|
};
|
|
@@ -507,75 +698,197 @@ function useDeployedDefinitions() {
|
|
|
507
698
|
};
|
|
508
699
|
}
|
|
509
700
|
|
|
510
|
-
const
|
|
701
|
+
const NO_TITLES = {
|
|
702
|
+
titles: /* @__PURE__ */ new Map,
|
|
703
|
+
resolved: /* @__PURE__ */ new Set
|
|
704
|
+
};
|
|
511
705
|
|
|
512
|
-
function
|
|
513
|
-
const {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
706
|
+
function useDocPreviewTitles(documents) {
|
|
707
|
+
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("|");
|
|
708
|
+
return useEffect(() => {
|
|
709
|
+
const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, previews = /* @__PURE__ */ new Map, publish = () => setState({
|
|
710
|
+
titles: new Map(titles),
|
|
711
|
+
resolved: new Set(resolved)
|
|
712
|
+
}), localDocs = documents.flatMap(doc => {
|
|
713
|
+
const {parsed: parsed, local: local} = gdrLocality(doc.id, binding.contentResource);
|
|
714
|
+
return !parsed || !local ? (resolved.add(doc.id), []) : [ {
|
|
715
|
+
doc: doc,
|
|
716
|
+
bareId: parsed.documentId
|
|
717
|
+
} ];
|
|
718
|
+
}), untracks = localDocs.map(({bareId: bareId}) => actualTypes.track(bareId)), dropPreview = docId => {
|
|
719
|
+
previews.get(docId)?.unsubscribe(), previews.delete(docId), titles.delete(docId);
|
|
720
|
+
}, observe = ({docId: docId, bareId: bareId, typeName: typeName}) => {
|
|
721
|
+
const schemaType = openableSchemaType(schema, typeName);
|
|
722
|
+
if (!schemaType) return !1;
|
|
723
|
+
if (previews.get(docId)?.typeName === typeName) return !0;
|
|
724
|
+
dropPreview(docId), resolved.delete(docId);
|
|
725
|
+
const subscription = previewStore.observeForPreview({
|
|
726
|
+
_id: bareId,
|
|
727
|
+
_type: schemaType.name
|
|
728
|
+
}, schemaType, {
|
|
729
|
+
perspective: perspectiveStack
|
|
730
|
+
}).subscribe({
|
|
731
|
+
next: event => {
|
|
732
|
+
resolved.add(docId);
|
|
733
|
+
const title = event.snapshot?.title;
|
|
734
|
+
typeof title == "string" ? titles.set(docId, title) : titles.delete(docId), publish();
|
|
735
|
+
},
|
|
736
|
+
error: err => {
|
|
737
|
+
console.error(`[workflow-studio-plugin] preview for "${docId}" failed:`, err), resolved.add(docId),
|
|
738
|
+
publish();
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
return previews.set(docId, {
|
|
742
|
+
typeName: typeName,
|
|
743
|
+
unsubscribe: () => subscription.unsubscribe()
|
|
744
|
+
}), !0;
|
|
745
|
+
}, evaluate = () => {
|
|
746
|
+
for (const {doc: doc, bareId: bareId} of localDocs) {
|
|
747
|
+
const probe = actualTypes.read(bareId);
|
|
748
|
+
if (probe === null) {
|
|
749
|
+
dropPreview(doc.id), resolved.add(doc.id);
|
|
750
|
+
continue;
|
|
751
|
+
}
|
|
752
|
+
!observe({
|
|
753
|
+
docId: doc.id,
|
|
754
|
+
bareId: bareId,
|
|
755
|
+
typeName: probe ?? doc.type
|
|
756
|
+
}) && typeof probe == "string" && (dropPreview(doc.id), resolved.add(doc.id));
|
|
757
|
+
}
|
|
758
|
+
publish();
|
|
759
|
+
}, unsubscribeProbes = actualTypes.subscribe(evaluate);
|
|
760
|
+
return evaluate(), () => {
|
|
761
|
+
unsubscribeProbes(), untracks.forEach(untrack => untrack()), previews.forEach(preview => preview.unsubscribe());
|
|
527
762
|
};
|
|
528
|
-
}, [
|
|
763
|
+
}, [ key, previewStore, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
|
|
764
|
+
state;
|
|
529
765
|
}
|
|
530
766
|
|
|
531
767
|
const INSTANCE_CAP_COUNT_NOTE = `Counts from the latest ${INSTANCE_CAP} workflows — older ones aren’t counted.`, NEWEST_INSTANCES = {
|
|
532
768
|
includeCompleted: !0
|
|
533
|
-
};
|
|
769
|
+
}, lastRead = /* @__PURE__ */ new WeakMap;
|
|
534
770
|
|
|
535
771
|
function useToolInstances() {
|
|
536
|
-
const {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable} = useCappedInstances(NEWEST_INSTANCES)
|
|
537
|
-
return useMemo(() => ({
|
|
772
|
+
const {engine: engine} = useWorkflowContext(), {rows: rows, truncated: truncated, loading: loading, unreadable: unreadable, error: error} = useCappedInstances(NEWEST_INSTANCES), fresh = useMemo(() => ({
|
|
538
773
|
inFlight: rows.filter(instance => terminalState(instance) === "in-flight"),
|
|
539
774
|
settled: rows.filter(instance => terminalState(instance) !== "in-flight"),
|
|
540
775
|
truncated: truncated,
|
|
541
776
|
loading: loading,
|
|
542
|
-
unreadable: unreadable
|
|
543
|
-
|
|
777
|
+
unreadable: unreadable,
|
|
778
|
+
error: error
|
|
779
|
+
}), [ rows, truncated, loading, unreadable, error ]);
|
|
780
|
+
return useEffect(() => {
|
|
781
|
+
!fresh.loading && fresh.error === void 0 && lastRead.set(engine, fresh);
|
|
782
|
+
}, [ engine, fresh ]), loading ? lastRead.get(engine) ?? fresh : fresh;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
function documentPhrase(count) {
|
|
786
|
+
return count === 0 ? "No documents" : pluralize("document", count, !0);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
function documentlessTail(count) {
|
|
790
|
+
return count === 0 ? "" : ` (${pluralize("instance", count, !0)})`;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
function documentlessNote(work) {
|
|
794
|
+
return work.documentCount > 0 ? "" : documentlessTail(work.documentlessCount);
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
function documentFace(work, atStart) {
|
|
798
|
+
const lead = `${documentPhrase(work.documentCount)} with an active workflow${documentlessNote(work)}`;
|
|
799
|
+
return atStart === 0 ? {
|
|
800
|
+
lead: lead,
|
|
801
|
+
startLine: void 0
|
|
802
|
+
} : atStart === work.documentCount ? {
|
|
803
|
+
lead: `${lead}, yet to leave the starting stage`,
|
|
804
|
+
startLine: void 0
|
|
805
|
+
} : {
|
|
806
|
+
lead: lead,
|
|
807
|
+
startLine: `${atStart} still at the starting stage`
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function overdueHint(work) {
|
|
812
|
+
return `${documentPhrase(work.documentCount)} with overdue tasks${documentlessTail(work.documentlessCount)}`;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
const UNTITLED_DOCUMENT = "Untitled", TRANSITION_HINT_PENDING = "Loading…";
|
|
816
|
+
|
|
817
|
+
function transitionHeading(at) {
|
|
818
|
+
return `Last stage change at ${formatShortDateTime(at)}`;
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function transitionFace(args) {
|
|
822
|
+
return args.documentPending ? {
|
|
823
|
+
state: "pending"
|
|
824
|
+
} : {
|
|
825
|
+
state: "ready",
|
|
826
|
+
document: args.documentTitle,
|
|
827
|
+
heading: transitionHeading(args.at),
|
|
828
|
+
lead: args.documentTitle === void 0 ? "Changed to" : "changed to",
|
|
829
|
+
stage: args.stage
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
function catalogView(args) {
|
|
834
|
+
if (args.error !== void 0) return {
|
|
835
|
+
kind: "unreadable"
|
|
836
|
+
};
|
|
837
|
+
if (args.definitions === void 0 || args.instances.loading || args.identity.kind === "pending") return {
|
|
838
|
+
kind: "loading"
|
|
839
|
+
};
|
|
840
|
+
if (args.definitions.length === 0) return {
|
|
841
|
+
kind: "empty"
|
|
842
|
+
};
|
|
843
|
+
const rows = definitionCatalog({
|
|
844
|
+
definitions: args.definitions,
|
|
845
|
+
identity: identityOf(args.identity),
|
|
846
|
+
instances: [ ...args.instances.inFlight, ...args.instances.settled ],
|
|
847
|
+
issues: args.issues,
|
|
848
|
+
mappings: args.mappings,
|
|
849
|
+
now: args.now
|
|
850
|
+
});
|
|
851
|
+
return {
|
|
852
|
+
kind: "bands",
|
|
853
|
+
bands: catalogBands(rows)
|
|
854
|
+
};
|
|
544
855
|
}
|
|
545
856
|
|
|
546
|
-
function
|
|
547
|
-
|
|
857
|
+
function definitionAlert(row) {
|
|
858
|
+
if (row.signals.stuckCount !== 0) return {
|
|
859
|
+
hint: `${pluralize("run", row.signals.stuckCount, !0)} here can’t continue without help`
|
|
860
|
+
};
|
|
548
861
|
}
|
|
549
862
|
|
|
550
|
-
const CARD_MIN_WIDTH = 280, CARD_RADIUS$1 = 3, CARD_PADDING = 4, CARD_MIN_HEIGHT =
|
|
863
|
+
const CARD_MIN_WIDTH = 280, CARD_RADIUS$1 = 3, CARD_PADDING = 4, HINT_DOCUMENT_MAX_WIDTH = 200, BAND_GAP = 6, CARD_MIN_HEIGHT = 120;
|
|
551
864
|
|
|
552
|
-
function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
553
|
-
const {definitions: definitions, error: error} = useDeployedDefinitions(), {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(),
|
|
865
|
+
function OverviewTab({identity: identity, onOpenWorkflow: onOpenWorkflow}) {
|
|
866
|
+
const {definitions: definitions, error: error} = useDeployedDefinitions(), {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), now = useMinuteClock(), view = catalogView({
|
|
867
|
+
definitions: definitions,
|
|
868
|
+
error: error,
|
|
869
|
+
identity: identity,
|
|
870
|
+
instances: instances,
|
|
871
|
+
issues: mappingIssues,
|
|
872
|
+
mappings: mappings,
|
|
873
|
+
now: now
|
|
874
|
+
}), undeployed = /* @__PURE__ */ jsx(UndeployedWorkflows, {
|
|
554
875
|
notices: undeployedWorkflowNotices({
|
|
555
876
|
issues: mappingIssues,
|
|
556
877
|
titleOf: name => schema.get(name)?.title
|
|
557
878
|
})
|
|
558
879
|
});
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
size: 1,
|
|
564
|
-
children: catalogReadError(error)
|
|
565
|
-
})
|
|
566
|
-
});
|
|
567
|
-
if (definitions === void 0) /* @__PURE__ */
|
|
568
|
-
return jsx(TabStatusRow, {
|
|
880
|
+
return view.kind === "unreadable" ? /* @__PURE__ */ jsx(NoteBanner, {
|
|
881
|
+
label: CATALOG_READ_FAILED,
|
|
882
|
+
tone: "critical"
|
|
883
|
+
}) : view.kind === "loading" ? /* @__PURE__ */ jsx(TabStatusRow, {
|
|
569
884
|
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
570
885
|
label: "Loading workflows…"
|
|
571
886
|
})
|
|
572
|
-
})
|
|
573
|
-
if (definitions.length === 0) /* @__PURE__ */
|
|
574
|
-
return jsxs(Flex, {
|
|
887
|
+
}) : view.kind === "empty" ? /* @__PURE__ */ jsxs(Flex, {
|
|
575
888
|
direction: "column",
|
|
576
889
|
flex: 1,
|
|
577
890
|
gap: 3,
|
|
578
|
-
paddingBottom:
|
|
891
|
+
paddingBottom: 3,
|
|
579
892
|
paddingTop: 3,
|
|
580
893
|
children: [ undeployed,
|
|
581
894
|
/* @__PURE__ */ jsx(EmptyState, {
|
|
@@ -583,67 +896,166 @@ function OverviewTab({onOpenWorkflow: onOpenWorkflow}) {
|
|
|
583
896
|
icon: /* @__PURE__ */ jsx(TransferIcon, {}),
|
|
584
897
|
title: "No workflows deployed"
|
|
585
898
|
}) ]
|
|
586
|
-
})
|
|
587
|
-
const rows = definitionCatalog({
|
|
588
|
-
definitions: definitions,
|
|
589
|
-
mappings: mappings,
|
|
590
|
-
inFlight: instances.inFlight,
|
|
591
|
-
issues: mappingIssues
|
|
592
|
-
}), active = rows.reduce((sum, row) => sum + row.activeCount, 0);
|
|
593
|
-
/* @__PURE__ */
|
|
594
|
-
return jsxs(Stack, {
|
|
899
|
+
}) : /* @__PURE__ */ jsxs(Stack, {
|
|
595
900
|
gap: 3,
|
|
596
901
|
paddingBottom: 4,
|
|
597
902
|
paddingTop: 3,
|
|
598
|
-
children: [ undeployed,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
gap: 2,
|
|
602
|
-
padding: TOOL_HEADING_PADDING,
|
|
603
|
-
children: [
|
|
604
|
-
/* @__PURE__ */ jsx(Text, {
|
|
605
|
-
size: 1,
|
|
606
|
-
weight: "semibold",
|
|
607
|
-
children: pluralize("workflow", rows.length, !0)
|
|
608
|
-
}),
|
|
609
|
-
/* @__PURE__ */ jsx(Text, {
|
|
610
|
-
muted: !0,
|
|
611
|
-
size: 1,
|
|
612
|
-
children: instances.loading ? "counting active instances…" : activeSummary(active)
|
|
613
|
-
}) ]
|
|
903
|
+
children: [ undeployed, instances.error === void 0 ? null : /* @__PURE__ */ jsx(NoteBanner, {
|
|
904
|
+
label: INSTANCES_READ_FAILED,
|
|
905
|
+
tone: "critical"
|
|
614
906
|
}),
|
|
615
|
-
/* @__PURE__ */ jsx(
|
|
616
|
-
gap:
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
907
|
+
/* @__PURE__ */ jsx(Stack, {
|
|
908
|
+
gap: BAND_GAP,
|
|
909
|
+
children: view.bands.map(band => /* @__PURE__ */ jsxs(Stack, {
|
|
910
|
+
gap: 3,
|
|
911
|
+
children: [ band.title === void 0 ? null : /* @__PURE__ */ jsx(Box, {
|
|
912
|
+
padding: TOOL_HEADING_PADDING,
|
|
913
|
+
children: /* @__PURE__ */ jsx(CountedHeading, {
|
|
914
|
+
count: band.rows.length,
|
|
915
|
+
title: band.title
|
|
916
|
+
})
|
|
917
|
+
}),
|
|
918
|
+
/* @__PURE__ */ jsx(CardGrid, {
|
|
919
|
+
countKnown: instances.error === void 0,
|
|
920
|
+
now: now,
|
|
921
|
+
onOpenWorkflow: onOpenWorkflow,
|
|
922
|
+
rows: band.rows
|
|
923
|
+
}) ]
|
|
924
|
+
}, band.title ?? UNBANDED_KEY))
|
|
626
925
|
}), instances.truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
627
926
|
text: INSTANCE_CAP_COUNT_NOTE
|
|
628
927
|
}) : null ]
|
|
629
928
|
});
|
|
630
929
|
}
|
|
631
930
|
|
|
632
|
-
const
|
|
931
|
+
const UNBANDED_KEY = "all";
|
|
932
|
+
|
|
933
|
+
function CardGrid({countKnown: countKnown, now: now, onOpenWorkflow: onOpenWorkflow, rows: rows}) {
|
|
934
|
+
/* @__PURE__ */
|
|
935
|
+
return jsx(Grid, {
|
|
936
|
+
gap: 3,
|
|
937
|
+
style: {
|
|
938
|
+
gridAutoRows: `minmax(${CARD_MIN_HEIGHT}px, auto)`,
|
|
939
|
+
gridTemplateColumns: `repeat(auto-fill, minmax(${CARD_MIN_WIDTH}px, 1fr))`
|
|
940
|
+
},
|
|
941
|
+
children: rows.map(row => /* @__PURE__ */ jsx(WorkflowCard, {
|
|
942
|
+
countKnown: countKnown,
|
|
943
|
+
now: now,
|
|
944
|
+
onOpen: () => onOpenWorkflow(row.name),
|
|
945
|
+
row: row
|
|
946
|
+
}, row.name))
|
|
947
|
+
});
|
|
948
|
+
}
|
|
633
949
|
|
|
634
950
|
function UndeployedWorkflows({notices: notices}) {
|
|
635
951
|
return notices.length === 0 ? null :
|
|
636
952
|
/* @__PURE__ */ jsx(Stack, {
|
|
637
953
|
gap: 2,
|
|
638
954
|
paddingBottom: 1,
|
|
639
|
-
children: notices.map(notice => /* @__PURE__ */ jsx(
|
|
955
|
+
children: notices.map(notice => /* @__PURE__ */ jsx(NoteBanner, {
|
|
640
956
|
label: notice
|
|
641
957
|
}, notice))
|
|
642
958
|
});
|
|
643
959
|
}
|
|
644
960
|
|
|
645
|
-
function
|
|
646
|
-
const
|
|
961
|
+
function LastTransitionHint({transition: transition}) {
|
|
962
|
+
const documents = useMemo(() => transition.document === void 0 ? [] : [ transition.document ], [ transition.document ]), {resolved: resolved, titles: titles} = useDocPreviewTitles(documents), id = transition.document?.id, face = transitionFace({
|
|
963
|
+
at: transition.at,
|
|
964
|
+
documentPending: id !== void 0 && !resolved.has(id),
|
|
965
|
+
documentTitle: id === void 0 ? void 0 : titles.get(id) ?? UNTITLED_DOCUMENT,
|
|
966
|
+
stage: transition.stage
|
|
967
|
+
});
|
|
968
|
+
return face.state === "pending" ? /* @__PURE__ */ jsx(Text, {
|
|
969
|
+
muted: !0,
|
|
970
|
+
size: 1,
|
|
971
|
+
children: TRANSITION_HINT_PENDING
|
|
972
|
+
}) :
|
|
973
|
+
/* @__PURE__ */ jsxs(Stack, {
|
|
974
|
+
gap: 3,
|
|
975
|
+
children: [
|
|
976
|
+
/* @__PURE__ */ jsx(Text, {
|
|
977
|
+
muted: !0,
|
|
978
|
+
size: 1,
|
|
979
|
+
children: face.heading
|
|
980
|
+
}),
|
|
981
|
+
/* @__PURE__ */ jsxs(Text, {
|
|
982
|
+
size: 1,
|
|
983
|
+
textOverflow: "ellipsis",
|
|
984
|
+
children: [ face.document === void 0 ? null : /* @__PURE__ */ jsx(HintDocument, {
|
|
985
|
+
children: face.document
|
|
986
|
+
}), face.document === void 0 ? "" : " ", face.lead, " ",
|
|
987
|
+
/* @__PURE__ */ jsx(InlineEmphasis, {
|
|
988
|
+
children: face.stage
|
|
989
|
+
}) ]
|
|
990
|
+
}) ]
|
|
991
|
+
});
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
function HintDocument({children: children}) {
|
|
995
|
+
/* @__PURE__ */
|
|
996
|
+
return jsx("span", {
|
|
997
|
+
style: {
|
|
998
|
+
display: "inline-block",
|
|
999
|
+
maxWidth: HINT_DOCUMENT_MAX_WIDTH,
|
|
1000
|
+
overflow: "hidden",
|
|
1001
|
+
textOverflow: "ellipsis",
|
|
1002
|
+
verticalAlign: "bottom",
|
|
1003
|
+
whiteSpace: "nowrap"
|
|
1004
|
+
},
|
|
1005
|
+
children: /* @__PURE__ */ jsx(InlineEmphasis, {
|
|
1006
|
+
children: children
|
|
1007
|
+
})
|
|
1008
|
+
});
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
function overdueWork(signals) {
|
|
1012
|
+
return {
|
|
1013
|
+
documentCount: signals.overdueDocumentCount,
|
|
1014
|
+
documentlessCount: signals.overdueDocumentlessCount
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
function CardMetrics$1({now: now, signals: signals}) {
|
|
1019
|
+
const overdue = overdueWork(signals), overdueTotal = overdue.documentCount + overdue.documentlessCount, documents = documentFace(signals, signals.initialStageDocumentCount);
|
|
1020
|
+
/* @__PURE__ */
|
|
1021
|
+
return jsxs(Flex, {
|
|
1022
|
+
align: "center",
|
|
1023
|
+
gap: 3,
|
|
1024
|
+
children: [
|
|
1025
|
+
/* @__PURE__ */ jsx(MetricPair, {
|
|
1026
|
+
Icon: DocumentIcon,
|
|
1027
|
+
empty: signals.documentCount === 0 && signals.documentlessCount === 0,
|
|
1028
|
+
face: {
|
|
1029
|
+
text: documents.lead,
|
|
1030
|
+
...documents.startLine === void 0 ? {} : {
|
|
1031
|
+
description: documents.startLine
|
|
1032
|
+
}
|
|
1033
|
+
},
|
|
1034
|
+
value: String(signals.documentCount)
|
|
1035
|
+
}), signals.lastTransition === void 0 ? null : /* @__PURE__ */ jsx(MetricPair, {
|
|
1036
|
+
Icon: ClockIcon,
|
|
1037
|
+
face: {
|
|
1038
|
+
content: /* @__PURE__ */ jsx(LastTransitionHint, {
|
|
1039
|
+
transition: signals.lastTransition
|
|
1040
|
+
}),
|
|
1041
|
+
wide: !0
|
|
1042
|
+
},
|
|
1043
|
+
value: formatShortAgo(signals.lastTransition.at, now)
|
|
1044
|
+
}), overdueTotal === 0 ? null : /* @__PURE__ */ jsx(MetricPair, {
|
|
1045
|
+
Icon: CalendarIcon,
|
|
1046
|
+
face: {
|
|
1047
|
+
text: overdueHint(overdue)
|
|
1048
|
+
},
|
|
1049
|
+
tone: "caution",
|
|
1050
|
+
value: String(overdueTotal)
|
|
1051
|
+
}) ]
|
|
1052
|
+
});
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
function WorkflowCard({countKnown: countKnown, now: now, onOpen: onOpen, row: row}) {
|
|
1056
|
+
const alert = definitionAlert(row);
|
|
1057
|
+
/* @__PURE__ */
|
|
1058
|
+
return jsxs(Card, {
|
|
647
1059
|
as: "button",
|
|
648
1060
|
onClick: onOpen,
|
|
649
1061
|
padding: CARD_PADDING,
|
|
@@ -656,50 +1068,44 @@ function WorkflowCard({countPending: countPending, onOpen: onOpen, row: row}) {
|
|
|
656
1068
|
textAlign: "left",
|
|
657
1069
|
width: "100%"
|
|
658
1070
|
},
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
} : {},
|
|
662
|
-
children: /* @__PURE__ */ jsxs(Flex, {
|
|
1071
|
+
children: [
|
|
1072
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
663
1073
|
align: "flex-start",
|
|
664
|
-
gap:
|
|
665
|
-
|
|
666
|
-
size: 1,
|
|
667
|
-
children: /* @__PURE__ */ jsx(WarningOutlineIcon, {})
|
|
668
|
-
}) : null,
|
|
669
|
-
/* @__PURE__ */ jsxs(Stack, {
|
|
1074
|
+
gap: 2,
|
|
1075
|
+
style: {
|
|
670
1076
|
flex: 1,
|
|
671
|
-
|
|
1077
|
+
minWidth: 0
|
|
1078
|
+
},
|
|
1079
|
+
children: [
|
|
1080
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1081
|
+
size: 1,
|
|
672
1082
|
style: {
|
|
1083
|
+
flex: 1,
|
|
673
1084
|
minWidth: 0
|
|
674
1085
|
},
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
muted: !0,
|
|
682
|
-
size: 1,
|
|
683
|
-
children: activeSummary(row.activeCount)
|
|
684
|
-
}) ]
|
|
1086
|
+
weight: "medium",
|
|
1087
|
+
children: row.title
|
|
1088
|
+
}), alert === void 0 ? null : /* @__PURE__ */ jsx(AlertGlyph, {
|
|
1089
|
+
Icon: ErrorOutlineIcon,
|
|
1090
|
+
hint: alert.hint,
|
|
1091
|
+
tone: "critical"
|
|
685
1092
|
}) ]
|
|
686
|
-
})
|
|
1093
|
+
}), countKnown ? /* @__PURE__ */ jsx(CardMetrics$1, {
|
|
1094
|
+
now: now,
|
|
1095
|
+
signals: row.signals
|
|
1096
|
+
}) : null ]
|
|
687
1097
|
});
|
|
688
|
-
return flagged ? /* @__PURE__ */ jsx(HoverHint, {
|
|
689
|
-
fill: !0,
|
|
690
|
-
text: CARD_ISSUES_HINT,
|
|
691
|
-
children: card
|
|
692
|
-
}) : card;
|
|
693
1098
|
}
|
|
694
1099
|
|
|
695
1100
|
function useStartableDefinitions() {
|
|
696
|
-
const {definitions: definitions, error: error} = useDeployedDefinitions()
|
|
1101
|
+
const {definitions: definitions, error: error} = useDeployedDefinitions(), startable = useMemo(() => definitions?.filter(row => isStartableDefinition(row)).map(row => ({
|
|
1102
|
+
name: row.name,
|
|
1103
|
+
title: row.title ?? row.name,
|
|
1104
|
+
description: row.description
|
|
1105
|
+
})), [ definitions ]);
|
|
697
1106
|
return {
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
title: row.title ?? row.name,
|
|
701
|
-
description: row.description
|
|
702
|
-
})), [ definitions ]),
|
|
1107
|
+
catalogEmpty: definitions?.length === 0,
|
|
1108
|
+
definitions: startable,
|
|
703
1109
|
error: error
|
|
704
1110
|
};
|
|
705
1111
|
}
|
|
@@ -731,16 +1137,45 @@ function NewWorkflowButton() {
|
|
|
731
1137
|
});
|
|
732
1138
|
}
|
|
733
1139
|
|
|
1140
|
+
const PANEL_WIDTH = 340;
|
|
1141
|
+
|
|
1142
|
+
function NothingToStart({catalogEmpty: catalogEmpty}) {
|
|
1143
|
+
/* @__PURE__ */
|
|
1144
|
+
return jsx(Box, {
|
|
1145
|
+
padding: 4,
|
|
1146
|
+
style: {
|
|
1147
|
+
width: PANEL_WIDTH
|
|
1148
|
+
},
|
|
1149
|
+
children: /* @__PURE__ */ jsxs(Stack, {
|
|
1150
|
+
gap: 3,
|
|
1151
|
+
children: [
|
|
1152
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1153
|
+
align: "center",
|
|
1154
|
+
size: 1,
|
|
1155
|
+
weight: "medium",
|
|
1156
|
+
children: catalogEmpty ? "No workflows deployed" : "Nothing to start by hand"
|
|
1157
|
+
}),
|
|
1158
|
+
/* @__PURE__ */ jsx(Text, {
|
|
1159
|
+
align: "center",
|
|
1160
|
+
muted: !0,
|
|
1161
|
+
size: 1,
|
|
1162
|
+
children: catalogEmpty ? "Deploy a workflow to this project to start one from here" : "Every deployed workflow here is started by a parent workflow, never by hand"
|
|
1163
|
+
}) ]
|
|
1164
|
+
})
|
|
1165
|
+
});
|
|
1166
|
+
}
|
|
1167
|
+
|
|
734
1168
|
function DefinitionPickerPanel({onPick: onPick}) {
|
|
735
|
-
const {definitions: definitions, error: error} = useStartableDefinitions(), [query, setQuery] = useState(""), visible = useMemo(() => {
|
|
1169
|
+
const {catalogEmpty: catalogEmpty, definitions: definitions, error: error} = useStartableDefinitions(), [query, setQuery] = useState(""), visible = useMemo(() => {
|
|
736
1170
|
const q = query.trim().toLowerCase();
|
|
737
1171
|
return definitions ? q ? definitions.filter(d => `${d.title} ${d.name}`.toLowerCase().includes(q)) : definitions : [];
|
|
738
|
-
}, [ definitions, query ]);
|
|
739
|
-
/* @__PURE__ */
|
|
740
|
-
|
|
1172
|
+
}, [ definitions, query ]), settled = error === void 0 ? definitions : void 0;
|
|
1173
|
+
return settled?.length === 0 ? /* @__PURE__ */ jsx(NothingToStart, {
|
|
1174
|
+
catalogEmpty: catalogEmpty
|
|
1175
|
+
}) : /* @__PURE__ */ jsx(Box, {
|
|
741
1176
|
padding: 2,
|
|
742
1177
|
style: {
|
|
743
|
-
width:
|
|
1178
|
+
width: PANEL_WIDTH
|
|
744
1179
|
},
|
|
745
1180
|
children: /* @__PURE__ */ jsxs(Stack, {
|
|
746
1181
|
gap: 2,
|
|
@@ -752,13 +1187,12 @@ function DefinitionPickerPanel({onPick: onPick}) {
|
|
|
752
1187
|
onChange: event => setQuery(event.currentTarget.value),
|
|
753
1188
|
placeholder: "Filter workflows…",
|
|
754
1189
|
value: query
|
|
755
|
-
}), error ? /* @__PURE__ */ jsx(
|
|
756
|
-
|
|
1190
|
+
}), error ? /* @__PURE__ */ jsx(NoteBanner, {
|
|
1191
|
+
label: CATALOG_READ_FAILED,
|
|
1192
|
+
tone: "critical"
|
|
757
1193
|
}) : null, !definitions && !error ? /* @__PURE__ */ jsx(LoadingRow, {
|
|
758
1194
|
label: "Loading workflows…"
|
|
759
|
-
}) : null,
|
|
760
|
-
text: "No startable workflows are deployed"
|
|
761
|
-
}) : null, definitions && definitions.length > 0 && visible.length === 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
1195
|
+
}) : null, settled !== void 0 && settled.length > 0 && visible.length === 0 ? /* @__PURE__ */ jsx(MutedNote, {
|
|
762
1196
|
text: "No workflows match"
|
|
763
1197
|
}) : null,
|
|
764
1198
|
/* @__PURE__ */ jsx(Stack, {
|
|
@@ -1036,13 +1470,20 @@ function useBandOpen(bands, key) {
|
|
|
1036
1470
|
};
|
|
1037
1471
|
}
|
|
1038
1472
|
|
|
1039
|
-
const
|
|
1473
|
+
const REVEAL_STALL_MS = 2e3, REVEAL_COLD_MS = 15e3;
|
|
1040
1474
|
|
|
1041
1475
|
function useRevealGate(args) {
|
|
1042
|
-
const {loading: loading, ready: ready} = args, settled = !loading && ready,
|
|
1476
|
+
const {loading: loading, progress: progress, ready: ready} = args, settled = !loading && ready, [stalled, setStalled] = useState(!1);
|
|
1477
|
+
useEffect(() => {
|
|
1478
|
+
if (loading || ready) return;
|
|
1479
|
+
setStalled(!1);
|
|
1480
|
+
const budget = progress > 0 ? REVEAL_STALL_MS : REVEAL_COLD_MS, timer = setTimeout(() => setStalled(!0), budget);
|
|
1481
|
+
return () => clearTimeout(timer);
|
|
1482
|
+
}, [ loading, progress, ready ]);
|
|
1483
|
+
const [revealed, setRevealed] = useState(!1);
|
|
1043
1484
|
return useEffect(() => {
|
|
1044
|
-
!revealed && (settled ||
|
|
1045
|
-
}, [
|
|
1485
|
+
!revealed && (settled || stalled) && setRevealed(!0);
|
|
1486
|
+
}, [ revealed, settled, stalled ]), revealed || settled || stalled;
|
|
1046
1487
|
}
|
|
1047
1488
|
|
|
1048
1489
|
function spawnOrigins(instances) {
|
|
@@ -1055,11 +1496,6 @@ function spawnOrigins(instances) {
|
|
|
1055
1496
|
return origins;
|
|
1056
1497
|
}
|
|
1057
1498
|
|
|
1058
|
-
function rootOf(instance, byId) {
|
|
1059
|
-
const rootRef = instance.ancestors[0];
|
|
1060
|
-
return rootRef ? byId.get(toBareId(rootRef.id)) ?? instance : instance;
|
|
1061
|
-
}
|
|
1062
|
-
|
|
1063
1499
|
function taskRowsOf(args) {
|
|
1064
1500
|
const {instance: instance, identity: identity} = args, open = findOpenStageEntry(instance);
|
|
1065
1501
|
if (!open) return [];
|
|
@@ -1089,6 +1525,10 @@ function taskRowsOf(args) {
|
|
|
1089
1525
|
state: fields,
|
|
1090
1526
|
who: identity
|
|
1091
1527
|
}),
|
|
1528
|
+
roleMatched: identity !== void 0 && isRoleAssignedTo({
|
|
1529
|
+
state: fields,
|
|
1530
|
+
who: identity
|
|
1531
|
+
}),
|
|
1092
1532
|
assignControl: rowAssignControlState(controlArgs),
|
|
1093
1533
|
dateControl: rowDateControlState(controlArgs)
|
|
1094
1534
|
};
|
|
@@ -1115,7 +1555,7 @@ function deriveTaskGroups(args) {
|
|
|
1115
1555
|
activityName: origin.activityName
|
|
1116
1556
|
}).title : void 0,
|
|
1117
1557
|
rows: rows
|
|
1118
|
-
}, root =
|
|
1558
|
+
}, root = rootInstanceOf(instance, byId), documents = documentRefsOf(root), document2 = documents[0];
|
|
1119
1559
|
if (!document2) {
|
|
1120
1560
|
withoutDocuments.push(entry);
|
|
1121
1561
|
continue;
|
|
@@ -1171,6 +1611,11 @@ function onlyMyTasks(groups) {
|
|
|
1171
1611
|
});
|
|
1172
1612
|
}
|
|
1173
1613
|
|
|
1614
|
+
function hasRoleMatchedTasks(groups) {
|
|
1615
|
+
const bandHasOne = band => band.rows.some(row => row.roleMatched);
|
|
1616
|
+
return groups.withDocuments.some(group => group.instances.some(bandHasOne)) || groups.withoutDocuments.some(bandHasOne);
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1174
1619
|
function rowActivityEval(entry, row) {
|
|
1175
1620
|
const evaluation = entry?.evaluation;
|
|
1176
1621
|
if (!(evaluation === void 0 || evaluation.currentStage.stage.name !== row.stage)) return evaluation.currentStage.activities.find(a => a.activity.name === row.activityName);
|
|
@@ -1314,7 +1759,7 @@ function OrphanedWorkflowsNote({orphans: orphans}) {
|
|
|
1314
1759
|
/* @__PURE__ */
|
|
1315
1760
|
return jsxs(Fragment, {
|
|
1316
1761
|
children: [
|
|
1317
|
-
/* @__PURE__ */ jsx(
|
|
1762
|
+
/* @__PURE__ */ jsx(NoteBanner, {
|
|
1318
1763
|
action: /* @__PURE__ */ jsx(Button, {
|
|
1319
1764
|
fontSize: 1,
|
|
1320
1765
|
mode: "ghost",
|
|
@@ -1416,72 +1861,6 @@ function SettleOrphansDialog({workflows: workflows, onClose: onClose}) {
|
|
|
1416
1861
|
});
|
|
1417
1862
|
}
|
|
1418
1863
|
|
|
1419
|
-
const NO_TITLES = {
|
|
1420
|
-
titles: /* @__PURE__ */ new Map,
|
|
1421
|
-
resolved: /* @__PURE__ */ new Set
|
|
1422
|
-
};
|
|
1423
|
-
|
|
1424
|
-
function useDocPreviewTitles(documents) {
|
|
1425
|
-
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("|");
|
|
1426
|
-
return useEffect(() => {
|
|
1427
|
-
const titles = /* @__PURE__ */ new Map, resolved = /* @__PURE__ */ new Set, previews = /* @__PURE__ */ new Map, publish = () => setState({
|
|
1428
|
-
titles: new Map(titles),
|
|
1429
|
-
resolved: new Set(resolved)
|
|
1430
|
-
}), localDocs = documents.flatMap(doc => {
|
|
1431
|
-
const {parsed: parsed, local: local} = gdrLocality(doc.id, binding.contentResource);
|
|
1432
|
-
return !parsed || !local ? (resolved.add(doc.id), []) : [ {
|
|
1433
|
-
doc: doc,
|
|
1434
|
-
bareId: parsed.documentId
|
|
1435
|
-
} ];
|
|
1436
|
-
}), untracks = localDocs.map(({bareId: bareId}) => actualTypes.track(bareId)), dropPreview = docId => {
|
|
1437
|
-
previews.get(docId)?.unsubscribe(), previews.delete(docId), titles.delete(docId);
|
|
1438
|
-
}, observe = ({docId: docId, bareId: bareId, typeName: typeName}) => {
|
|
1439
|
-
const schemaType = openableSchemaType(schema, typeName);
|
|
1440
|
-
if (!schemaType) return !1;
|
|
1441
|
-
if (previews.get(docId)?.typeName === typeName) return !0;
|
|
1442
|
-
dropPreview(docId), resolved.delete(docId);
|
|
1443
|
-
const subscription = previewStore.observeForPreview({
|
|
1444
|
-
_id: bareId,
|
|
1445
|
-
_type: schemaType.name
|
|
1446
|
-
}, schemaType, {
|
|
1447
|
-
perspective: perspectiveStack
|
|
1448
|
-
}).subscribe({
|
|
1449
|
-
next: event => {
|
|
1450
|
-
resolved.add(docId);
|
|
1451
|
-
const title = event.snapshot?.title;
|
|
1452
|
-
typeof title == "string" ? titles.set(docId, title) : titles.delete(docId), publish();
|
|
1453
|
-
},
|
|
1454
|
-
error: err => {
|
|
1455
|
-
console.error(`[workflow-studio-plugin] preview for "${docId}" failed:`, err), resolved.add(docId),
|
|
1456
|
-
publish();
|
|
1457
|
-
}
|
|
1458
|
-
});
|
|
1459
|
-
return previews.set(docId, {
|
|
1460
|
-
typeName: typeName,
|
|
1461
|
-
unsubscribe: () => subscription.unsubscribe()
|
|
1462
|
-
}), !0;
|
|
1463
|
-
}, evaluate = () => {
|
|
1464
|
-
for (const {doc: doc, bareId: bareId} of localDocs) {
|
|
1465
|
-
const probe = actualTypes.read(bareId);
|
|
1466
|
-
if (probe === null) {
|
|
1467
|
-
dropPreview(doc.id), resolved.add(doc.id);
|
|
1468
|
-
continue;
|
|
1469
|
-
}
|
|
1470
|
-
!observe({
|
|
1471
|
-
docId: doc.id,
|
|
1472
|
-
bareId: bareId,
|
|
1473
|
-
typeName: probe ?? doc.type
|
|
1474
|
-
}) && typeof probe == "string" && (dropPreview(doc.id), resolved.add(doc.id));
|
|
1475
|
-
}
|
|
1476
|
-
publish();
|
|
1477
|
-
}, unsubscribeProbes = actualTypes.subscribe(evaluate);
|
|
1478
|
-
return evaluate(), () => {
|
|
1479
|
-
unsubscribeProbes(), untracks.forEach(untrack => untrack()), previews.forEach(preview => preview.unsubscribe());
|
|
1480
|
-
};
|
|
1481
|
-
}, [ key, previewStore, schema, actualTypes, binding.contentResource.id, perspectiveStack.join("|") ]),
|
|
1482
|
-
state;
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
1864
|
function taskFilterOptions(groups) {
|
|
1486
1865
|
const workflows = /* @__PURE__ */ new Map, documents = /* @__PURE__ */ new Map;
|
|
1487
1866
|
for (const group of groups.withDocuments) {
|
|
@@ -2650,7 +3029,7 @@ function useOrphanedWorkflows(groups) {
|
|
|
2650
3029
|
}), [ groups, contentResource, missing ]);
|
|
2651
3030
|
}
|
|
2652
3031
|
|
|
2653
|
-
function TasksTab({filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
|
|
3032
|
+
function TasksTab({error: error, filterSlot: filterSlot, instances: instances, loading: loading, identity: identity, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, tab: tab, truncated: truncated, unreadable: unreadable}) {
|
|
2654
3033
|
const mineOnly = tab === "for-me", {binding: binding} = useWorkflowContext(), bands = useBandOpenState({
|
|
2655
3034
|
segment: TASK_BAND_SEGMENTS[tab],
|
|
2656
3035
|
scope: binding.engineResource.id
|
|
@@ -2659,6 +3038,7 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
2659
3038
|
identity: identity
|
|
2660
3039
|
}), [ instances, identity ]), orphans = useOrphanedWorkflows(groups), listed = useMemo(() => hideOrphanedGroups(groups, orphans.groupKeys), [ groups, orphans.groupKeys ]), options = useMemo(() => taskFilterOptions(listed), [ listed ]), titles = useGroupTitles(listed), revealed = useRevealGate({
|
|
2661
3040
|
loading: loading,
|
|
3041
|
+
progress: titles.resolved.size,
|
|
2662
3042
|
ready: titles.ready
|
|
2663
3043
|
}), visible = useMemo(() => {
|
|
2664
3044
|
const filtered = applyTaskFilters({
|
|
@@ -2668,6 +3048,10 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
2668
3048
|
}), cut = mineOnly ? onlyMyTasks(filtered) : filtered;
|
|
2669
3049
|
return orderGroupsByPreviewTitle(cut, titles.titles);
|
|
2670
3050
|
}, [ listed, filters, mineOnly, titles ]);
|
|
3051
|
+
if (error !== void 0) /* @__PURE__ */ return jsx(NoteBanner, {
|
|
3052
|
+
label: INSTANCES_READ_FAILED,
|
|
3053
|
+
tone: "critical"
|
|
3054
|
+
});
|
|
2671
3055
|
if (loading || !revealed) /* @__PURE__ */
|
|
2672
3056
|
return jsx(TabStatusRow, {
|
|
2673
3057
|
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
@@ -2707,13 +3091,37 @@ function TasksTab({filterSlot: filterSlot, instances: instances, loading: loadin
|
|
|
2707
3091
|
}),
|
|
2708
3092
|
/* @__PURE__ */ jsx(OrphanedWorkflowsNote, {
|
|
2709
3093
|
orphans: orphans
|
|
2710
|
-
}), body, truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
3094
|
+
}), mineOnly && hasRoleMatchedTasks(visible) ? /* @__PURE__ */ jsx(RoleAssignedNotice, {}) : null, body, truncated ? /* @__PURE__ */ jsx(MutedNote, {
|
|
2711
3095
|
text: `Tasks from the latest ${INSTANCE_CAP} workflows — older ones aren’t listed.`
|
|
2712
3096
|
}) : null ]
|
|
2713
3097
|
}) ]
|
|
2714
3098
|
});
|
|
2715
3099
|
}
|
|
2716
3100
|
|
|
3101
|
+
function cardFacts(args) {
|
|
3102
|
+
return new Map(args.instances.map(instance => [ instance._id, {
|
|
3103
|
+
pending: openActivitiesOf(instance).length,
|
|
3104
|
+
overdue: overdueTaskCount(instance, args.now),
|
|
3105
|
+
stuck: documentStuckCause({
|
|
3106
|
+
instance: instance,
|
|
3107
|
+
definition: definitionSnapshotOf(instance)
|
|
3108
|
+
}) !== void 0,
|
|
3109
|
+
settled: terminalState(instance) !== "in-flight"
|
|
3110
|
+
} ]));
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
function laneTotals(cards, facts) {
|
|
3114
|
+
let overdue = 0, stuck = 0;
|
|
3115
|
+
for (const card of cards) {
|
|
3116
|
+
const held = facts.get(card.instanceId);
|
|
3117
|
+
held !== void 0 && (held.overdue > 0 && (overdue += 1), held.stuck && (stuck += 1));
|
|
3118
|
+
}
|
|
3119
|
+
return {
|
|
3120
|
+
overdue: overdue,
|
|
3121
|
+
stuck: stuck
|
|
3122
|
+
};
|
|
3123
|
+
}
|
|
3124
|
+
|
|
2717
3125
|
const BOARD_EMPTY_STAGE_NOTE = "No items";
|
|
2718
3126
|
|
|
2719
3127
|
function faceOf(instance) {
|
|
@@ -2772,6 +3180,24 @@ function boardDocuments(board) {
|
|
|
2772
3180
|
return board.groups.flatMap(group => group.cards.flatMap(card => card.face.kind === "document" ? [ card.face.document ] : []));
|
|
2773
3181
|
}
|
|
2774
3182
|
|
|
3183
|
+
function pendingHint(pending) {
|
|
3184
|
+
return pending === 0 ? "All tasks done" : `${pluralize("task", pending, !0)} still open`;
|
|
3185
|
+
}
|
|
3186
|
+
|
|
3187
|
+
function overdueTasksHint(overdue) {
|
|
3188
|
+
return `${pluralize("task", overdue, !0)} past ${overdue === 1 ? "its" : "their"} due date`;
|
|
3189
|
+
}
|
|
3190
|
+
|
|
3191
|
+
const STUCK_RUN_HINT = "This run can’t continue without help";
|
|
3192
|
+
|
|
3193
|
+
function laneOverdueHint(overdue) {
|
|
3194
|
+
return `${overdue} of these ${overdue === 1 ? "has" : "have"} overdue tasks`;
|
|
3195
|
+
}
|
|
3196
|
+
|
|
3197
|
+
function laneStuckHint(stuck) {
|
|
3198
|
+
return `${stuck} of these can’t continue without help`;
|
|
3199
|
+
}
|
|
3200
|
+
|
|
2775
3201
|
function CardTitleFace({title: title}) {
|
|
2776
3202
|
return title.kind === "title" ? /* @__PURE__ */ jsx(Fragment, {
|
|
2777
3203
|
children: title.title
|
|
@@ -2798,23 +3224,10 @@ function cardTitle(args) {
|
|
|
2798
3224
|
};
|
|
2799
3225
|
}
|
|
2800
3226
|
|
|
2801
|
-
function ownOpenTasks(instance) {
|
|
2802
|
-
const open = findOpenStageEntry(instance);
|
|
2803
|
-
return open ? (open.activities ?? []).filter(activity => isOpenActivityStatus(activity.status)).length : 0;
|
|
2804
|
-
}
|
|
2805
|
-
|
|
2806
|
-
function pendingTaskCounts(instances) {
|
|
2807
|
-
return new Map(instances.map(instance => [ instance._id, ownOpenTasks(instance) ]));
|
|
2808
|
-
}
|
|
2809
|
-
|
|
2810
|
-
function pendingTasksPhrase(pending) {
|
|
2811
|
-
if (pending !== void 0) return pending === 0 ? "All tasks done" : pluralize("pending task", pending, !0);
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
3227
|
const CARD_RADIUS = 3;
|
|
2815
3228
|
|
|
2816
3229
|
function BoardCard({card: card, wiring: wiring}) {
|
|
2817
|
-
const {
|
|
3230
|
+
const {facts: facts, onSelect: onSelect, selectedInstanceId: selectedInstanceId, titles: titles} = wiring, {color: color} = useTheme_v2(), selected = card.instanceId === selectedInstanceId, held = facts.get(card.instanceId);
|
|
2818
3231
|
/* @__PURE__ */
|
|
2819
3232
|
return jsx(Card, {
|
|
2820
3233
|
"aria-pressed": selected,
|
|
@@ -2832,54 +3245,93 @@ function BoardCard({card: card, wiring: wiring}) {
|
|
|
2832
3245
|
children: /* @__PURE__ */ jsxs(Stack, {
|
|
2833
3246
|
gap: 3,
|
|
2834
3247
|
children: [
|
|
2835
|
-
/* @__PURE__ */
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
children:
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
3248
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
3249
|
+
align: "flex-start",
|
|
3250
|
+
gap: 2,
|
|
3251
|
+
children: [
|
|
3252
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3253
|
+
size: 1,
|
|
3254
|
+
style: {
|
|
3255
|
+
flex: 1,
|
|
3256
|
+
minWidth: 0
|
|
3257
|
+
},
|
|
3258
|
+
weight: "medium",
|
|
3259
|
+
children: /* @__PURE__ */ jsx(CardTitleFace, {
|
|
3260
|
+
title: cardTitle({
|
|
3261
|
+
face: card.face,
|
|
3262
|
+
titles: titles
|
|
3263
|
+
})
|
|
2842
3264
|
})
|
|
2843
|
-
})
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
3265
|
+
}), held?.stuck === !0 ? /* @__PURE__ */ jsx(AlertGlyph, {
|
|
3266
|
+
Icon: ErrorOutlineIcon,
|
|
3267
|
+
hint: STUCK_RUN_HINT,
|
|
3268
|
+
tone: "critical"
|
|
3269
|
+
}) : null ]
|
|
3270
|
+
}), held === void 0 ? null : /* @__PURE__ */ jsx(CardMetrics, {
|
|
3271
|
+
facts: held
|
|
2847
3272
|
}) ]
|
|
2848
3273
|
})
|
|
2849
3274
|
});
|
|
2850
3275
|
}
|
|
2851
3276
|
|
|
2852
|
-
function
|
|
2853
|
-
|
|
2854
|
-
return phrase === void 0 ? null : /* @__PURE__ */ jsx(Flex, {
|
|
3277
|
+
function CardMetrics({facts: facts}) {
|
|
3278
|
+
return facts.settled ? null : /* @__PURE__ */ jsxs(Flex, {
|
|
2855
3279
|
align: "center",
|
|
2856
|
-
|
|
2857
|
-
children:
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
3280
|
+
gap: 3,
|
|
3281
|
+
children: [
|
|
3282
|
+
/* @__PURE__ */ jsx(MetricPair, {
|
|
3283
|
+
Icon: EmptyIcon,
|
|
3284
|
+
empty: facts.pending === 0,
|
|
3285
|
+
face: {
|
|
3286
|
+
text: pendingHint(facts.pending)
|
|
3287
|
+
},
|
|
3288
|
+
value: String(facts.pending)
|
|
3289
|
+
}), facts.overdue === 0 ? null : /* @__PURE__ */ jsx(MetricPair, {
|
|
3290
|
+
Icon: CalendarIcon,
|
|
3291
|
+
face: {
|
|
3292
|
+
text: overdueTasksHint(facts.overdue)
|
|
3293
|
+
},
|
|
3294
|
+
tone: "caution",
|
|
3295
|
+
value: String(facts.overdue)
|
|
3296
|
+
}) ]
|
|
2867
3297
|
});
|
|
2868
3298
|
}
|
|
2869
3299
|
|
|
2870
|
-
function StageGroupHeading({group: group}) {
|
|
3300
|
+
function StageGroupHeading({facts: facts, group: group}) {
|
|
3301
|
+
const totals = laneTotals(group.cards, facts);
|
|
2871
3302
|
/* @__PURE__ */
|
|
2872
3303
|
return jsxs(Flex, {
|
|
2873
3304
|
align: "center",
|
|
2874
3305
|
gap: 2,
|
|
2875
3306
|
children: [
|
|
2876
|
-
/* @__PURE__ */ jsx(
|
|
3307
|
+
/* @__PURE__ */ jsx(CountedHeading, {
|
|
2877
3308
|
count: group.cards.length,
|
|
2878
|
-
|
|
2879
|
-
|
|
2880
|
-
|
|
2881
|
-
|
|
2882
|
-
|
|
3309
|
+
title: group.title
|
|
3310
|
+
}), totals.overdue === 0 && totals.stuck === 0 ? null : /* @__PURE__ */ jsxs(Fragment, {
|
|
3311
|
+
children: [
|
|
3312
|
+
/* @__PURE__ */ jsx(Flex, {
|
|
3313
|
+
flex: 1
|
|
3314
|
+
}),
|
|
3315
|
+
/* @__PURE__ */ jsxs(Flex, {
|
|
3316
|
+
align: "center",
|
|
3317
|
+
flex: "none",
|
|
3318
|
+
gap: 3,
|
|
3319
|
+
children: [ totals.overdue === 0 ? null : /* @__PURE__ */ jsx(MetricPair, {
|
|
3320
|
+
Icon: CalendarIcon,
|
|
3321
|
+
face: {
|
|
3322
|
+
text: laneOverdueHint(totals.overdue)
|
|
3323
|
+
},
|
|
3324
|
+
tone: "caution",
|
|
3325
|
+
value: String(totals.overdue)
|
|
3326
|
+
}), totals.stuck === 0 ? null : /* @__PURE__ */ jsx(MetricPair, {
|
|
3327
|
+
Icon: ErrorOutlineIcon,
|
|
3328
|
+
face: {
|
|
3329
|
+
text: laneStuckHint(totals.stuck)
|
|
3330
|
+
},
|
|
3331
|
+
tone: "critical",
|
|
3332
|
+
value: String(totals.stuck)
|
|
3333
|
+
}) ]
|
|
3334
|
+
}) ]
|
|
2883
3335
|
}), group.retired ?
|
|
2884
3336
|
/* @__PURE__ */ jsx(Text, {
|
|
2885
3337
|
muted: !0,
|
|
@@ -2928,6 +3380,7 @@ function StageColumn({group: group, leadingRule: leadingRule, trailingRule: trai
|
|
|
2928
3380
|
paddingTop: 4,
|
|
2929
3381
|
paddingX: LANE_PADDING,
|
|
2930
3382
|
children: /* @__PURE__ */ jsx(StageGroupHeading, {
|
|
3383
|
+
facts: wiring.facts,
|
|
2931
3384
|
group: group
|
|
2932
3385
|
})
|
|
2933
3386
|
}), group.cards.length === 0 ? /* @__PURE__ */ jsx(Flex, {
|
|
@@ -3068,7 +3521,7 @@ function useBoard(args) {
|
|
|
3068
3521
|
}), {titles: titles, ready: ready} = seeded, board = useMemo(() => derived ? orderBoardByPreviewTitle(derived, titles) : void 0, [ derived, titles ]);
|
|
3069
3522
|
return useMemo(() => ({
|
|
3070
3523
|
board: board,
|
|
3071
|
-
ready: ready,
|
|
3524
|
+
ready: board !== void 0 && ready,
|
|
3072
3525
|
titles: seeded
|
|
3073
3526
|
}), [ board, ready, seeded ]);
|
|
3074
3527
|
}
|
|
@@ -3085,6 +3538,16 @@ function useBoardInstances(definitionName) {
|
|
|
3085
3538
|
return useCappedInstances(filter);
|
|
3086
3539
|
}
|
|
3087
3540
|
|
|
3541
|
+
function activeCountFace(args) {
|
|
3542
|
+
return args.error !== void 0 || args.loading ? {
|
|
3543
|
+
count: void 0,
|
|
3544
|
+
note: void 0
|
|
3545
|
+
} : {
|
|
3546
|
+
count: args.count,
|
|
3547
|
+
note: args.truncated ? INSTANCE_CAP_COUNT_NOTE : void 0
|
|
3548
|
+
};
|
|
3549
|
+
}
|
|
3550
|
+
|
|
3088
3551
|
function DefinitionPanel({definition: definition}) {
|
|
3089
3552
|
/* @__PURE__ */
|
|
3090
3553
|
return jsx(Box, {
|
|
@@ -3147,12 +3610,31 @@ function SetupIssues({bindings: bindings}) {
|
|
|
3147
3610
|
});
|
|
3148
3611
|
}
|
|
3149
3612
|
|
|
3613
|
+
function ActiveCount({face: face}) {
|
|
3614
|
+
/* @__PURE__ */
|
|
3615
|
+
return jsxs(Stack, {
|
|
3616
|
+
gap: 2,
|
|
3617
|
+
children: [
|
|
3618
|
+
/* @__PURE__ */ jsx(Text, {
|
|
3619
|
+
muted: face.count === void 0,
|
|
3620
|
+
size: 1,
|
|
3621
|
+
children: face.count ?? "—"
|
|
3622
|
+
}), face.note === void 0 ? null : /* @__PURE__ */ jsx(Text, {
|
|
3623
|
+
muted: !0,
|
|
3624
|
+
size: 1,
|
|
3625
|
+
children: face.note
|
|
3626
|
+
}) ]
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
|
|
3150
3630
|
function DefinitionFacts({definition: definition}) {
|
|
3151
|
-
const {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), snapshotWidth = useContainerToken(2), [row] = definitionCatalog({
|
|
3631
|
+
const {mappingIssues: mappingIssues, mappings: mappings} = useWorkflowContext(), schema = useSchema(), instances = useToolInstances(), now = useMinuteClock(), snapshotWidth = useContainerToken(2), [row] = definitionCatalog({
|
|
3152
3632
|
definitions: [ definition ],
|
|
3633
|
+
identity: void 0,
|
|
3153
3634
|
mappings: mappings,
|
|
3154
|
-
|
|
3155
|
-
issues: mappingIssues
|
|
3635
|
+
instances: [ ...instances.inFlight, ...instances.settled ],
|
|
3636
|
+
issues: mappingIssues,
|
|
3637
|
+
now: now
|
|
3156
3638
|
}), runsOn = docTypeLabels(row?.docTypes ?? [], name => schema.get(name)?.title).map(docType => docType.title);
|
|
3157
3639
|
/* @__PURE__ */
|
|
3158
3640
|
return jsxs(Flex, {
|
|
@@ -3164,6 +3646,12 @@ function DefinitionFacts({definition: definition}) {
|
|
|
3164
3646
|
children: /* @__PURE__ */ jsx(SetupIssues, {
|
|
3165
3647
|
bindings: row.brokenBindings
|
|
3166
3648
|
})
|
|
3649
|
+
}), instances.error === void 0 ? null : /* @__PURE__ */ jsx(Box, {
|
|
3650
|
+
flex: "none",
|
|
3651
|
+
children: /* @__PURE__ */ jsx(NoteBanner, {
|
|
3652
|
+
label: INSTANCES_READ_FAILED,
|
|
3653
|
+
tone: "critical"
|
|
3654
|
+
})
|
|
3167
3655
|
}),
|
|
3168
3656
|
/* @__PURE__ */ jsx(Box, {
|
|
3169
3657
|
flex: "none",
|
|
@@ -3182,21 +3670,13 @@ function DefinitionFacts({definition: definition}) {
|
|
|
3182
3670
|
}),
|
|
3183
3671
|
/* @__PURE__ */ jsx(MetaRow, {
|
|
3184
3672
|
label: "Active instances",
|
|
3185
|
-
children:
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
/* @__PURE__ */ jsx(Text, {
|
|
3193
|
-
size: 1,
|
|
3194
|
-
children: row?.activeCount ?? 0
|
|
3195
|
-
}), instances.truncated ? /* @__PURE__ */ jsx(Text, {
|
|
3196
|
-
muted: !0,
|
|
3197
|
-
size: 1,
|
|
3198
|
-
children: INSTANCE_CAP_COUNT_NOTE
|
|
3199
|
-
}) : null ]
|
|
3673
|
+
children: /* @__PURE__ */ jsx(ActiveCount, {
|
|
3674
|
+
face: activeCountFace({
|
|
3675
|
+
count: row?.signals.activeCount ?? 0,
|
|
3676
|
+
error: instances.error,
|
|
3677
|
+
loading: instances.loading,
|
|
3678
|
+
truncated: instances.truncated
|
|
3679
|
+
})
|
|
3200
3680
|
})
|
|
3201
3681
|
}),
|
|
3202
3682
|
/* @__PURE__ */ jsx(MetaRow, {
|
|
@@ -3260,7 +3740,7 @@ function pickerLabel(face) {
|
|
|
3260
3740
|
}
|
|
3261
3741
|
|
|
3262
3742
|
function WorkflowPicker({face: face, onSelect: onSelect, options: options}) {
|
|
3263
|
-
const empty = options.length === 0;
|
|
3743
|
+
const empty = options.length === 0, reclaimed = -useSpaceToken(TOOL_HEADING_PADDING);
|
|
3264
3744
|
/* @__PURE__ */
|
|
3265
3745
|
return jsx(MenuButton, {
|
|
3266
3746
|
button: /* @__PURE__ */ jsx(Button, {
|
|
@@ -3270,6 +3750,9 @@ function WorkflowPicker({face: face, onSelect: onSelect, options: options}) {
|
|
|
3270
3750
|
iconRight: ChevronDownIcon,
|
|
3271
3751
|
mode: "bleed",
|
|
3272
3752
|
padding: TOOL_HEADING_PADDING,
|
|
3753
|
+
style: {
|
|
3754
|
+
margin: reclaimed
|
|
3755
|
+
},
|
|
3273
3756
|
text: pickerLabel(face),
|
|
3274
3757
|
textWeight: "semibold"
|
|
3275
3758
|
}),
|
|
@@ -3300,6 +3783,7 @@ function WorkflowPageHeader({face: face, onSelectPage: onSelectPage, onSelectWor
|
|
|
3300
3783
|
children: [
|
|
3301
3784
|
/* @__PURE__ */ jsx(Flex, {
|
|
3302
3785
|
align: "center",
|
|
3786
|
+
padding: TOOL_HEADING_PADDING,
|
|
3303
3787
|
children: /* @__PURE__ */ jsx(WorkflowPicker, {
|
|
3304
3788
|
face: face,
|
|
3305
3789
|
onSelect: onSelectWorkflow,
|
|
@@ -3733,30 +4217,38 @@ function usePageSelection(page) {
|
|
|
3733
4217
|
}
|
|
3734
4218
|
|
|
3735
4219
|
function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPage, page: page, workflowName: workflowName}) {
|
|
3736
|
-
const {definitions: definitions, error:
|
|
4220
|
+
const {definitions: definitions, error: catalogError} = useDeployedDefinitions(), selection = resolveWorkflowSelection({
|
|
3737
4221
|
definitions: definitions,
|
|
3738
4222
|
routeName: workflowName
|
|
3739
4223
|
}), selected = resolvedDefinition(selection), view = workflowPageView({
|
|
3740
|
-
error:
|
|
4224
|
+
error: catalogError,
|
|
3741
4225
|
page: page,
|
|
3742
4226
|
selection: selection
|
|
3743
4227
|
}), instances = useBoardInstances(view.streamName), {board: board, ready: ready, titles: titles} = useBoard({
|
|
3744
4228
|
definition: selected,
|
|
3745
4229
|
instances: instances.rows
|
|
3746
|
-
}), {select: select, selectedInstanceId: selectedInstanceId} = usePageSelection(page), panel = usePanelPresence(panelSubject({
|
|
4230
|
+
}), {select: select, selectedInstanceId: selectedInstanceId} = usePageSelection(page), now = useMinuteClock(), panel = usePanelPresence(panelSubject({
|
|
3747
4231
|
board: board,
|
|
3748
4232
|
selectedInstanceId: selectedInstanceId
|
|
3749
4233
|
})), revealed = useRevealGate({
|
|
3750
4234
|
loading: instances.loading,
|
|
4235
|
+
progress: titles.resolved.size,
|
|
3751
4236
|
ready: ready
|
|
3752
|
-
})
|
|
4237
|
+
});
|
|
3753
4238
|
if (selection.kind === "loading") /* @__PURE__ */ return jsx(UnresolvedPage, {
|
|
3754
|
-
error:
|
|
3755
|
-
|
|
4239
|
+
error: catalogError
|
|
4240
|
+
});
|
|
4241
|
+
const catalogEmpty = (definitions ?? []).length === 0;
|
|
4242
|
+
if (selection.kind === "unknown" && catalogEmpty) /* @__PURE__ */
|
|
4243
|
+
return jsx(EmptyCatalogPage, {
|
|
4244
|
+
name: selection.name
|
|
3756
4245
|
});
|
|
3757
4246
|
const wiring = {
|
|
3758
4247
|
onSelect: select,
|
|
3759
|
-
|
|
4248
|
+
facts: cardFacts({
|
|
4249
|
+
instances: instances.rows,
|
|
4250
|
+
now: now
|
|
4251
|
+
}),
|
|
3760
4252
|
selectedInstanceId: selectedInstanceId,
|
|
3761
4253
|
titles: titles
|
|
3762
4254
|
};
|
|
@@ -3809,8 +4301,9 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
|
|
|
3809
4301
|
}),
|
|
3810
4302
|
/* @__PURE__ */ jsx(PageBody, {
|
|
3811
4303
|
board: board,
|
|
3812
|
-
catalogEmpty:
|
|
3813
|
-
|
|
4304
|
+
catalogEmpty: catalogEmpty,
|
|
4305
|
+
catalogError: catalogError,
|
|
4306
|
+
instancesError: instances.error,
|
|
3814
4307
|
loading: !revealed,
|
|
3815
4308
|
page: page,
|
|
3816
4309
|
selection: selection,
|
|
@@ -3838,45 +4331,66 @@ function WorkflowPage({onOpenWorkflow: onOpenWorkflow, onSelectPage: onSelectPag
|
|
|
3838
4331
|
});
|
|
3839
4332
|
}
|
|
3840
4333
|
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
function UnresolvedPage({error: error, waiting: waiting}) {
|
|
4334
|
+
function UnresolvedPage({error: error}) {
|
|
3844
4335
|
/* @__PURE__ */
|
|
3845
4336
|
return jsx(Flex, {
|
|
3846
4337
|
direction: "column",
|
|
3847
4338
|
height: "fill",
|
|
3848
4339
|
overflow: "hidden",
|
|
3849
|
-
children: error === void 0 ? /* @__PURE__ */ jsx(WaitingFace, {
|
|
3850
|
-
|
|
3851
|
-
}) : /* @__PURE__ */ jsx(CatalogErrorFace, {
|
|
3852
|
-
error: error
|
|
4340
|
+
children: error === void 0 ? /* @__PURE__ */ jsx(WaitingFace, {}) : /* @__PURE__ */ jsx(ReadErrorFace, {
|
|
4341
|
+
text: CATALOG_READ_FAILED
|
|
3853
4342
|
})
|
|
3854
4343
|
});
|
|
3855
4344
|
}
|
|
3856
4345
|
|
|
3857
|
-
function WaitingFace(
|
|
4346
|
+
function WaitingFace() {
|
|
3858
4347
|
/* @__PURE__ */
|
|
3859
4348
|
return jsx(StatusBox, {
|
|
3860
|
-
children:
|
|
4349
|
+
children: /* @__PURE__ */ jsx(TabStatusRow, {
|
|
3861
4350
|
children: /* @__PURE__ */ jsx(LoadingRow, {
|
|
3862
4351
|
label: "Loading workflow…"
|
|
3863
4352
|
})
|
|
3864
|
-
})
|
|
4353
|
+
})
|
|
4354
|
+
});
|
|
4355
|
+
}
|
|
4356
|
+
|
|
4357
|
+
function EmptyCatalogPage({name: name}) {
|
|
4358
|
+
const face = unknownWorkflowFace({
|
|
4359
|
+
catalogEmpty: !0,
|
|
4360
|
+
name: name
|
|
4361
|
+
});
|
|
4362
|
+
/* @__PURE__ */
|
|
4363
|
+
return jsx(Flex, {
|
|
4364
|
+
direction: "column",
|
|
4365
|
+
height: "fill",
|
|
4366
|
+
overflow: "hidden",
|
|
4367
|
+
padding: LANE_PADDING,
|
|
4368
|
+
children: /* @__PURE__ */ jsx(EmptyState, {
|
|
4369
|
+
description: face.description,
|
|
4370
|
+
icon: /* @__PURE__ */ jsx(SearchIcon, {}),
|
|
4371
|
+
title: face.title
|
|
4372
|
+
})
|
|
3865
4373
|
});
|
|
3866
4374
|
}
|
|
3867
4375
|
|
|
3868
|
-
|
|
4376
|
+
const BANNER_LANE_INSET = 2;
|
|
4377
|
+
|
|
4378
|
+
function ReadErrorFace({text: text}) {
|
|
3869
4379
|
/* @__PURE__ */
|
|
3870
4380
|
return jsx(StatusBox, {
|
|
3871
|
-
children: /* @__PURE__ */ jsx(
|
|
3872
|
-
|
|
4381
|
+
children: /* @__PURE__ */ jsx(Box, {
|
|
4382
|
+
paddingTop: BANNER_LANE_INSET,
|
|
4383
|
+
children: /* @__PURE__ */ jsx(NoteBanner, {
|
|
4384
|
+
label: text,
|
|
4385
|
+
tone: "critical"
|
|
4386
|
+
})
|
|
3873
4387
|
})
|
|
3874
4388
|
});
|
|
3875
4389
|
}
|
|
3876
4390
|
|
|
3877
|
-
function PageBody({board: board, catalogEmpty: catalogEmpty,
|
|
3878
|
-
if (
|
|
3879
|
-
|
|
4391
|
+
function PageBody({board: board, catalogEmpty: catalogEmpty, catalogError: catalogError, instancesError: instancesError, loading: loading, page: page, selection: selection, wiring: wiring}) {
|
|
4392
|
+
if (catalogError !== void 0) /* @__PURE__ */ return jsx(ReadErrorFace, {
|
|
4393
|
+
text: CATALOG_READ_FAILED
|
|
3880
4394
|
});
|
|
3881
4395
|
if (selection.kind === "unknown") {
|
|
3882
4396
|
const face = unknownWorkflowFace({
|
|
@@ -3894,6 +4408,8 @@ function PageBody({board: board, catalogEmpty: catalogEmpty, error: error, loadi
|
|
|
3894
4408
|
}
|
|
3895
4409
|
return page === "definition" ? /* @__PURE__ */ jsx(DefinitionPanel, {
|
|
3896
4410
|
definition: selection.definition
|
|
4411
|
+
}) : instancesError !== void 0 ? /* @__PURE__ */ jsx(ReadErrorFace, {
|
|
4412
|
+
text: INSTANCES_READ_FAILED
|
|
3897
4413
|
}) : loading || board === void 0 ? /* @__PURE__ */ jsx(StatusBox, {
|
|
3898
4414
|
children: /* @__PURE__ */ jsx(Box, {
|
|
3899
4415
|
paddingTop: LANE_PADDING,
|
|
@@ -4058,10 +4574,12 @@ function WorkflowsHome({filterSlot: filterSlot, onOpenInstance: onOpenInstance,
|
|
|
4058
4574
|
|
|
4059
4575
|
function HomePanel({filterSlot: filterSlot, identity: identity, instances: instances, onOpenInstance: onOpenInstance, onOpenTask: onOpenTask, onOpenWorkflow: onOpenWorkflow, tab: tab}) {
|
|
4060
4576
|
return tab === "overview" ? /* @__PURE__ */ jsx(OverviewTab, {
|
|
4577
|
+
identity: identity,
|
|
4061
4578
|
onOpenWorkflow: onOpenWorkflow
|
|
4062
4579
|
}) : /* @__PURE__ */ jsx(TasksTab, {
|
|
4580
|
+
error: instances.error,
|
|
4063
4581
|
filterSlot: filterSlot,
|
|
4064
|
-
identity: identity,
|
|
4582
|
+
identity: identityOf(identity),
|
|
4065
4583
|
instances: [ ...instances.inFlight, ...instances.settled ],
|
|
4066
4584
|
loading: instances.loading,
|
|
4067
4585
|
onOpenInstance: onOpenInstance,
|