@memberjunction/ng-conversations 5.51.0 → 6.1.0-edge.1
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/LICENSE +7 -0
- package/dist/lib/components/conversation/conversation-chat-area.component.js +1 -1
- package/dist/lib/components/conversation/conversation-chat-area.component.js.map +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js +1 -1
- package/dist/lib/components/message/conversation-message-rating.component.js.map +1 -1
- package/dist/lib/components/message/message-input.component.d.ts +17 -7
- package/dist/lib/components/message/message-input.component.d.ts.map +1 -1
- package/dist/lib/components/message/message-input.component.js +56 -179
- package/dist/lib/components/message/message-input.component.js.map +1 -1
- package/dist/lib/components/message/workflow-plan-card.component.d.ts +67 -0
- package/dist/lib/components/message/workflow-plan-card.component.d.ts.map +1 -0
- package/dist/lib/components/message/workflow-plan-card.component.js +166 -0
- package/dist/lib/components/message/workflow-plan-card.component.js.map +1 -0
- package/dist/lib/components/task/tasks-full-view.component.d.ts +8 -0
- package/dist/lib/components/task/tasks-full-view.component.d.ts.map +1 -1
- package/dist/lib/components/task/tasks-full-view.component.js +14 -62
- package/dist/lib/components/task/tasks-full-view.component.js.map +1 -1
- package/dist/lib/conversations.module.d.ts +74 -73
- package/dist/lib/conversations.module.d.ts.map +1 -1
- package/dist/lib/conversations.module.js +8 -4
- package/dist/lib/conversations.module.js.map +1 -1
- package/dist/lib/models/lazy-artifact-info.d.ts +14 -2
- package/dist/lib/models/lazy-artifact-info.d.ts.map +1 -1
- package/dist/lib/models/lazy-artifact-info.js +10 -2
- package/dist/lib/models/lazy-artifact-info.js.map +1 -1
- package/dist/lib/services/mention-autocomplete.service.js +3 -3
- package/dist/lib/services/mention-autocomplete.service.js.map +1 -1
- package/dist/public-api.d.ts +1 -0
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +1 -0
- package/dist/public-api.js.map +1 -1
- package/package.json +44 -41
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The plan card in a conversation: what an agent decomposed, and what became of it.
|
|
3
|
+
*
|
|
4
|
+
* **The moment this exists for.** An agent breaks a request into steps, the work runs, and it was
|
|
5
|
+
* good. Today that is where it ends — the decomposition was ephemeral, so the next person who wants
|
|
6
|
+
* the same thing asks an agent to invent it again. This card is where a one-off agent plan becomes
|
|
7
|
+
* reusable organizational automation (D17).
|
|
8
|
+
*
|
|
9
|
+
* Renders through the *same* `TaskGraphSpec` component the editor uses, in `ReadOnly` mode. That is
|
|
10
|
+
* deliberate: a second, simpler renderer for chat would be a second thing that can disagree with the
|
|
11
|
+
* canvas about what a graph means, and the whole program has been about removing exactly that.
|
|
12
|
+
*
|
|
13
|
+
* Save is **intent only**. This is a `widgets`-layer component in a conversation; it does not
|
|
14
|
+
* persist agents. The host converts and writes through `AgentSpecSync`, which keeps the one place
|
|
15
|
+
* that writes an agent the one place that writes an agent.
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/ng-conversations
|
|
18
|
+
*/
|
|
19
|
+
import { EventEmitter } from '@angular/core';
|
|
20
|
+
import type { TaskGraphSpec } from '@memberjunction/ai-core-plus';
|
|
21
|
+
import type { TaskGraphRuntimeStatus } from '@memberjunction/ng-task-graph-editor';
|
|
22
|
+
import * as i0 from "@angular/core";
|
|
23
|
+
/** Fired when the user asks to promote this plan into a reusable workflow. */
|
|
24
|
+
export declare class SaveAsWorkflowRequestedEventArgs {
|
|
25
|
+
readonly Spec: TaskGraphSpec;
|
|
26
|
+
/** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
|
|
27
|
+
readonly ParentTaskID: string | null;
|
|
28
|
+
constructor(Spec: TaskGraphSpec,
|
|
29
|
+
/** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
|
|
30
|
+
ParentTaskID: string | null);
|
|
31
|
+
}
|
|
32
|
+
export declare class WorkflowPlanCardComponent {
|
|
33
|
+
/** The plan. Null renders nothing — a card with no graph has nothing to say. */
|
|
34
|
+
Spec: TaskGraphSpec | null;
|
|
35
|
+
/** Live per-step state. Absent means the card shows the plan without a progress line. */
|
|
36
|
+
RuntimeStatus: TaskGraphRuntimeStatus | null;
|
|
37
|
+
/** Handle for status/cancel, when the graph was dispatched rather than folded. */
|
|
38
|
+
ParentTaskID: string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Whether to offer Save as Workflow.
|
|
41
|
+
*
|
|
42
|
+
* Off while work is still running: offering to save a plan whose shape may yet change — a
|
|
43
|
+
* retry, a failure routing down a recovery branch — invites saving something that never
|
|
44
|
+
* actually happened.
|
|
45
|
+
*/
|
|
46
|
+
AllowSaveAsWorkflow: boolean;
|
|
47
|
+
/** Starts collapsed. A plan card sits inline in a conversation and must not dominate it. */
|
|
48
|
+
Expanded: boolean;
|
|
49
|
+
SaveAsWorkflowRequested: EventEmitter<SaveAsWorkflowRequestedEventArgs>;
|
|
50
|
+
ExpandedChange: EventEmitter<boolean>;
|
|
51
|
+
get StepCount(): number;
|
|
52
|
+
get TempIds(): string[];
|
|
53
|
+
/** One line of progress, or the plan's own reasoning when nothing is running yet. */
|
|
54
|
+
get SummaryLine(): string;
|
|
55
|
+
/** True once every step has reached a state nothing will move it out of. */
|
|
56
|
+
get IsFinished(): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Save is offered only once the work has settled — or when there is no runtime at all, which is
|
|
59
|
+
* the design-time case (a plan being previewed before it runs).
|
|
60
|
+
*/
|
|
61
|
+
get CanSave(): boolean;
|
|
62
|
+
ToggleExpanded(): void;
|
|
63
|
+
RequestSave(): void;
|
|
64
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<WorkflowPlanCardComponent, never>;
|
|
65
|
+
static ɵcmp: i0.ɵɵComponentDeclaration<WorkflowPlanCardComponent, "mj-workflow-plan-card", never, { "Spec": { "alias": "Spec"; "required": false; }; "RuntimeStatus": { "alias": "RuntimeStatus"; "required": false; }; "ParentTaskID": { "alias": "ParentTaskID"; "required": false; }; "AllowSaveAsWorkflow": { "alias": "AllowSaveAsWorkflow"; "required": false; }; "Expanded": { "alias": "Expanded"; "required": false; }; }, { "SaveAsWorkflowRequested": "SaveAsWorkflowRequested"; "ExpandedChange": "ExpandedChange"; }, never, never, false, never>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=workflow-plan-card.component.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-plan-card.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AACH,OAAO,EAAa,YAAY,EAAiB,MAAM,eAAe,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sCAAsC,CAAC;;AAGnF,8EAA8E;AAC9E,qBAAa,gCAAgC;aAErB,IAAI,EAAE,aAAa;IACnC,2FAA2F;aAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;gBAF3B,IAAI,EAAE,aAAa;IACnC,2FAA2F;IAC3E,YAAY,EAAE,MAAM,GAAG,IAAI;CAElD;AAED,qBAMa,yBAAyB;IAClC,gFAAgF;IAChE,IAAI,EAAE,aAAa,GAAG,IAAI,CAAQ;IAElD,yFAAyF;IACzE,aAAa,EAAE,sBAAsB,GAAG,IAAI,CAAQ;IAEpE,kFAAkF;IAClE,YAAY,EAAE,MAAM,GAAG,IAAI,CAAQ;IAEnD;;;;;;OAMG;IACa,mBAAmB,EAAE,OAAO,CAAQ;IAEpD,4FAA4F;IAC5E,QAAQ,EAAE,OAAO,CAAS;IAEzB,uBAAuB,iDAAwD;IAC/E,cAAc,wBAA+B;IAE9D,IAAW,SAAS,IAAI,MAAM,CAE7B;IAED,IAAW,OAAO,IAAI,MAAM,EAAE,CAE7B;IAED,qFAAqF;IACrF,IAAW,WAAW,IAAI,MAAM,CAG/B;IAED,4EAA4E;IAC5E,IAAW,UAAU,IAAI,OAAO,CAE/B;IAED;;;OAGG;IACH,IAAW,OAAO,IAAI,OAAO,CAE5B;IAEM,cAAc,IAAI,IAAI;IAKtB,WAAW,IAAI,IAAI;yCAzDjB,yBAAyB;2CAAzB,yBAAyB;CA8DrC"}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview The plan card in a conversation: what an agent decomposed, and what became of it.
|
|
3
|
+
*
|
|
4
|
+
* **The moment this exists for.** An agent breaks a request into steps, the work runs, and it was
|
|
5
|
+
* good. Today that is where it ends — the decomposition was ephemeral, so the next person who wants
|
|
6
|
+
* the same thing asks an agent to invent it again. This card is where a one-off agent plan becomes
|
|
7
|
+
* reusable organizational automation (D17).
|
|
8
|
+
*
|
|
9
|
+
* Renders through the *same* `TaskGraphSpec` component the editor uses, in `ReadOnly` mode. That is
|
|
10
|
+
* deliberate: a second, simpler renderer for chat would be a second thing that can disagree with the
|
|
11
|
+
* canvas about what a graph means, and the whole program has been about removing exactly that.
|
|
12
|
+
*
|
|
13
|
+
* Save is **intent only**. This is a `widgets`-layer component in a conversation; it does not
|
|
14
|
+
* persist agents. The host converts and writes through `AgentSpecSync`, which keeps the one place
|
|
15
|
+
* that writes an agent the one place that writes an agent.
|
|
16
|
+
*
|
|
17
|
+
* @module @memberjunction/ng-conversations
|
|
18
|
+
*/
|
|
19
|
+
import { Component, EventEmitter, Input, Output } from '@angular/core';
|
|
20
|
+
import { IsRuntimeSettled, SummarizeRuntime } from '@memberjunction/ng-task-graph-editor';
|
|
21
|
+
import * as i0 from "@angular/core";
|
|
22
|
+
import * as i1 from "@memberjunction/ng-task-graph-editor";
|
|
23
|
+
import * as i2 from "@memberjunction/ng-ui-components";
|
|
24
|
+
function WorkflowPlanCardComponent_Conditional_0_Conditional_7_Template(rf, ctx) { if (rf & 1) {
|
|
25
|
+
i0.ɵɵelement(0, "i", 6);
|
|
26
|
+
} }
|
|
27
|
+
function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
28
|
+
const _r3 = i0.ɵɵgetCurrentView();
|
|
29
|
+
i0.ɵɵelementStart(0, "div", 9)(1, "button", 10);
|
|
30
|
+
i0.ɵɵlistener("click", function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r1.RequestSave()); });
|
|
31
|
+
i0.ɵɵelement(2, "i", 11);
|
|
32
|
+
i0.ɵɵtext(3, " Save this approach as a Workflow ");
|
|
33
|
+
i0.ɵɵelementEnd()();
|
|
34
|
+
} }
|
|
35
|
+
function WorkflowPlanCardComponent_Conditional_0_Conditional_8_Template(rf, ctx) { if (rf & 1) {
|
|
36
|
+
i0.ɵɵelementStart(0, "div", 7);
|
|
37
|
+
i0.ɵɵelement(1, "mj-task-graph-editor", 8);
|
|
38
|
+
i0.ɵɵconditionalCreate(2, WorkflowPlanCardComponent_Conditional_0_Conditional_8_Conditional_2_Template, 4, 0, "div", 9);
|
|
39
|
+
i0.ɵɵelementEnd();
|
|
40
|
+
} if (rf & 2) {
|
|
41
|
+
const ctx_r1 = i0.ɵɵnextContext(2);
|
|
42
|
+
i0.ɵɵadvance();
|
|
43
|
+
i0.ɵɵproperty("Spec", ctx_r1.Spec)("RuntimeStatus", ctx_r1.RuntimeStatus)("ReadOnly", true)("ShowPalette", false)("ShowToolbar", false)("ShowMinimap", false);
|
|
44
|
+
i0.ɵɵadvance();
|
|
45
|
+
i0.ɵɵconditional(ctx_r1.CanSave ? 2 : -1);
|
|
46
|
+
} }
|
|
47
|
+
function WorkflowPlanCardComponent_Conditional_0_Template(rf, ctx) { if (rf & 1) {
|
|
48
|
+
const _r1 = i0.ɵɵgetCurrentView();
|
|
49
|
+
i0.ɵɵelementStart(0, "div", 1)(1, "button", 2);
|
|
50
|
+
i0.ɵɵlistener("click", function WorkflowPlanCardComponent_Conditional_0_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.ToggleExpanded()); });
|
|
51
|
+
i0.ɵɵelement(2, "i", 3);
|
|
52
|
+
i0.ɵɵelementStart(3, "span", 4);
|
|
53
|
+
i0.ɵɵtext(4);
|
|
54
|
+
i0.ɵɵelementEnd();
|
|
55
|
+
i0.ɵɵelementStart(5, "span", 5);
|
|
56
|
+
i0.ɵɵtext(6);
|
|
57
|
+
i0.ɵɵelementEnd();
|
|
58
|
+
i0.ɵɵconditionalCreate(7, WorkflowPlanCardComponent_Conditional_0_Conditional_7_Template, 1, 0, "i", 6);
|
|
59
|
+
i0.ɵɵelementEnd();
|
|
60
|
+
i0.ɵɵconditionalCreate(8, WorkflowPlanCardComponent_Conditional_0_Conditional_8_Template, 3, 7, "div", 7);
|
|
61
|
+
i0.ɵɵelementEnd();
|
|
62
|
+
} if (rf & 2) {
|
|
63
|
+
const ctx_r1 = i0.ɵɵnextContext();
|
|
64
|
+
i0.ɵɵclassProp("mj-wpc--expanded", ctx_r1.Expanded);
|
|
65
|
+
i0.ɵɵadvance();
|
|
66
|
+
i0.ɵɵattribute("aria-expanded", ctx_r1.Expanded);
|
|
67
|
+
i0.ɵɵadvance();
|
|
68
|
+
i0.ɵɵclassProp("fa-chevron-right", !ctx_r1.Expanded)("fa-chevron-down", ctx_r1.Expanded);
|
|
69
|
+
i0.ɵɵadvance(2);
|
|
70
|
+
i0.ɵɵtextInterpolate(ctx_r1.Spec.workflowName);
|
|
71
|
+
i0.ɵɵadvance(2);
|
|
72
|
+
i0.ɵɵtextInterpolate(ctx_r1.SummaryLine);
|
|
73
|
+
i0.ɵɵadvance();
|
|
74
|
+
i0.ɵɵconditional(ctx_r1.IsFinished ? 7 : -1);
|
|
75
|
+
i0.ɵɵadvance();
|
|
76
|
+
i0.ɵɵconditional(ctx_r1.Expanded ? 8 : -1);
|
|
77
|
+
} }
|
|
78
|
+
/** Fired when the user asks to promote this plan into a reusable workflow. */
|
|
79
|
+
export class SaveAsWorkflowRequestedEventArgs {
|
|
80
|
+
Spec;
|
|
81
|
+
ParentTaskID;
|
|
82
|
+
constructor(Spec,
|
|
83
|
+
/** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */
|
|
84
|
+
ParentTaskID) {
|
|
85
|
+
this.Spec = Spec;
|
|
86
|
+
this.ParentTaskID = ParentTaskID;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
export class WorkflowPlanCardComponent {
|
|
90
|
+
/** The plan. Null renders nothing — a card with no graph has nothing to say. */
|
|
91
|
+
Spec = null;
|
|
92
|
+
/** Live per-step state. Absent means the card shows the plan without a progress line. */
|
|
93
|
+
RuntimeStatus = null;
|
|
94
|
+
/** Handle for status/cancel, when the graph was dispatched rather than folded. */
|
|
95
|
+
ParentTaskID = null;
|
|
96
|
+
/**
|
|
97
|
+
* Whether to offer Save as Workflow.
|
|
98
|
+
*
|
|
99
|
+
* Off while work is still running: offering to save a plan whose shape may yet change — a
|
|
100
|
+
* retry, a failure routing down a recovery branch — invites saving something that never
|
|
101
|
+
* actually happened.
|
|
102
|
+
*/
|
|
103
|
+
AllowSaveAsWorkflow = true;
|
|
104
|
+
/** Starts collapsed. A plan card sits inline in a conversation and must not dominate it. */
|
|
105
|
+
Expanded = false;
|
|
106
|
+
SaveAsWorkflowRequested = new EventEmitter();
|
|
107
|
+
ExpandedChange = new EventEmitter();
|
|
108
|
+
get StepCount() {
|
|
109
|
+
return this.Spec?.tasks?.length ?? 0;
|
|
110
|
+
}
|
|
111
|
+
get TempIds() {
|
|
112
|
+
return (this.Spec?.tasks ?? []).map((t) => t.tempId);
|
|
113
|
+
}
|
|
114
|
+
/** One line of progress, or the plan's own reasoning when nothing is running yet. */
|
|
115
|
+
get SummaryLine() {
|
|
116
|
+
if (this.RuntimeStatus)
|
|
117
|
+
return SummarizeRuntime(this.RuntimeStatus, this.TempIds);
|
|
118
|
+
return this.Spec?.reasoning || `${this.StepCount} step${this.StepCount === 1 ? '' : 's'}`;
|
|
119
|
+
}
|
|
120
|
+
/** True once every step has reached a state nothing will move it out of. */
|
|
121
|
+
get IsFinished() {
|
|
122
|
+
return !!this.RuntimeStatus && IsRuntimeSettled(this.RuntimeStatus, this.TempIds);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Save is offered only once the work has settled — or when there is no runtime at all, which is
|
|
126
|
+
* the design-time case (a plan being previewed before it runs).
|
|
127
|
+
*/
|
|
128
|
+
get CanSave() {
|
|
129
|
+
return this.AllowSaveAsWorkflow && !!this.Spec && (!this.RuntimeStatus || this.IsFinished);
|
|
130
|
+
}
|
|
131
|
+
ToggleExpanded() {
|
|
132
|
+
this.Expanded = !this.Expanded;
|
|
133
|
+
this.ExpandedChange.emit(this.Expanded);
|
|
134
|
+
}
|
|
135
|
+
RequestSave() {
|
|
136
|
+
if (this.Spec && this.CanSave) {
|
|
137
|
+
this.SaveAsWorkflowRequested.emit(new SaveAsWorkflowRequestedEventArgs(this.Spec, this.ParentTaskID));
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
static ɵfac = function WorkflowPlanCardComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || WorkflowPlanCardComponent)(); };
|
|
141
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: WorkflowPlanCardComponent, selectors: [["mj-workflow-plan-card"]], inputs: { Spec: "Spec", RuntimeStatus: "RuntimeStatus", ParentTaskID: "ParentTaskID", AllowSaveAsWorkflow: "AllowSaveAsWorkflow", Expanded: "Expanded" }, outputs: { SaveAsWorkflowRequested: "SaveAsWorkflowRequested", ExpandedChange: "ExpandedChange" }, standalone: false, decls: 1, vars: 1, consts: [[1, "mj-wpc", 3, "mj-wpc--expanded"], [1, "mj-wpc"], ["type", "button", 1, "mj-wpc__header", 3, "click"], [1, "fa-solid"], [1, "mj-wpc__title"], [1, "mj-wpc__summary"], ["aria-label", "Finished", 1, "fa-solid", "fa-circle-check", "mj-wpc__done"], [1, "mj-wpc__body"], [1, "mj-wpc__canvas", 3, "Spec", "RuntimeStatus", "ReadOnly", "ShowPalette", "ShowToolbar", "ShowMinimap"], [1, "mj-wpc__actions"], ["mjButton", "", "variant", "primary", "size", "sm", 3, "click"], [1, "fa-solid", "fa-floppy-disk"]], template: function WorkflowPlanCardComponent_Template(rf, ctx) { if (rf & 1) {
|
|
142
|
+
i0.ɵɵconditionalCreate(0, WorkflowPlanCardComponent_Conditional_0_Template, 9, 11, "div", 0);
|
|
143
|
+
} if (rf & 2) {
|
|
144
|
+
i0.ɵɵconditional(ctx.Spec ? 0 : -1);
|
|
145
|
+
} }, dependencies: [i1.TaskGraphEditorComponent, i2.MJButtonDirective], styles: ["\n\n.mj-wpc[_ngcontent-%COMP%] {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title[_ngcontent-%COMP%] {\n font-weight: 600;\n}\n\n.mj-wpc__summary[_ngcontent-%COMP%] {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done[_ngcontent-%COMP%] {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body[_ngcontent-%COMP%] {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n\n\n.mj-wpc__canvas[_ngcontent-%COMP%] {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions[_ngcontent-%COMP%] {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}"] });
|
|
146
|
+
}
|
|
147
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(WorkflowPlanCardComponent, [{
|
|
148
|
+
type: Component,
|
|
149
|
+
args: [{ standalone: false, selector: 'mj-workflow-plan-card', template: "@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <div class=\"mj-wpc__actions\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"RequestSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save this approach as a Workflow\n </button>\n </div>\n }\n </div>\n }\n </div>\n}\n", styles: ["/* Design tokens only \u2014 no hardcoded colors. */\n.mj-wpc {\n border: 1px solid var(--mj-border-subtle);\n border-radius: var(--mj-radius-md, 6px);\n background: var(--mj-bg-surface);\n overflow: hidden;\n}\n\n.mj-wpc__header {\n display: flex;\n align-items: center;\n gap: var(--mj-space-sm, 0.5rem);\n width: 100%;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n background: transparent;\n border: none;\n cursor: pointer;\n text-align: left;\n color: var(--mj-text-primary);\n}\n\n.mj-wpc__title {\n font-weight: 600;\n}\n\n.mj-wpc__summary {\n flex: 1 1 auto;\n font-size: var(--mj-font-size-sm, 0.8125rem);\n color: var(--mj-text-secondary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mj-wpc__done {\n color: var(--mj-color-success);\n}\n\n.mj-wpc__body {\n display: flex;\n flex-direction: column;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* A plan card sits inline in a conversation \u2014 a bounded height keeps it from dominating the thread. */\n.mj-wpc__canvas {\n height: 320px;\n min-height: 0;\n}\n\n.mj-wpc__actions {\n display: flex;\n justify-content: flex-start;\n padding: var(--mj-space-sm, 0.5rem) var(--mj-space-md, 1rem);\n border-top: 1px solid var(--mj-border-subtle);\n}\n"] }]
|
|
150
|
+
}], null, { Spec: [{
|
|
151
|
+
type: Input
|
|
152
|
+
}], RuntimeStatus: [{
|
|
153
|
+
type: Input
|
|
154
|
+
}], ParentTaskID: [{
|
|
155
|
+
type: Input
|
|
156
|
+
}], AllowSaveAsWorkflow: [{
|
|
157
|
+
type: Input
|
|
158
|
+
}], Expanded: [{
|
|
159
|
+
type: Input
|
|
160
|
+
}], SaveAsWorkflowRequested: [{
|
|
161
|
+
type: Output
|
|
162
|
+
}], ExpandedChange: [{
|
|
163
|
+
type: Output
|
|
164
|
+
}] }); })();
|
|
165
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(WorkflowPlanCardComponent, { className: "WorkflowPlanCardComponent", filePath: "src/lib/components/message/workflow-plan-card.component.ts", lineNumber: 39 }); })();
|
|
166
|
+
//# sourceMappingURL=workflow-plan-card.component.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-plan-card.component.js","sourceRoot":"","sources":["../../../../src/lib/components/message/workflow-plan-card.component.ts","../../../../src/lib/components/message/workflow-plan-card.component.html"],"names":[],"mappings":"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","sourcesContent":["/**\n * @fileoverview The plan card in a conversation: what an agent decomposed, and what became of it.\n *\n * **The moment this exists for.** An agent breaks a request into steps, the work runs, and it was\n * good. Today that is where it ends — the decomposition was ephemeral, so the next person who wants\n * the same thing asks an agent to invent it again. This card is where a one-off agent plan becomes\n * reusable organizational automation (D17).\n *\n * Renders through the *same* `TaskGraphSpec` component the editor uses, in `ReadOnly` mode. That is\n * deliberate: a second, simpler renderer for chat would be a second thing that can disagree with the\n * canvas about what a graph means, and the whole program has been about removing exactly that.\n *\n * Save is **intent only**. This is a `widgets`-layer component in a conversation; it does not\n * persist agents. The host converts and writes through `AgentSpecSync`, which keeps the one place\n * that writes an agent the one place that writes an agent.\n *\n * @module @memberjunction/ng-conversations\n */\nimport { Component, EventEmitter, Input, Output } from '@angular/core';\nimport type { TaskGraphSpec } from '@memberjunction/ai-core-plus';\nimport type { TaskGraphRuntimeStatus } from '@memberjunction/ng-task-graph-editor';\nimport { IsRuntimeSettled, SummarizeRuntime } from '@memberjunction/ng-task-graph-editor';\n\n/** Fired when the user asks to promote this plan into a reusable workflow. */\nexport class SaveAsWorkflowRequestedEventArgs {\n constructor(\n public readonly Spec: TaskGraphSpec,\n /** The parent task the graph ran as, when it was dispatched. Absent for a folded graph. */\n public readonly ParentTaskID: string | null,\n ) {}\n}\n\n@Component({\n standalone: false,\n selector: 'mj-workflow-plan-card',\n templateUrl: './workflow-plan-card.component.html',\n styleUrls: ['./workflow-plan-card.component.css'],\n})\nexport class WorkflowPlanCardComponent {\n /** The plan. Null renders nothing — a card with no graph has nothing to say. */\n @Input() public Spec: TaskGraphSpec | null = null;\n\n /** Live per-step state. Absent means the card shows the plan without a progress line. */\n @Input() public RuntimeStatus: TaskGraphRuntimeStatus | null = null;\n\n /** Handle for status/cancel, when the graph was dispatched rather than folded. */\n @Input() public ParentTaskID: string | null = null;\n\n /**\n * Whether to offer Save as Workflow.\n *\n * Off while work is still running: offering to save a plan whose shape may yet change — a\n * retry, a failure routing down a recovery branch — invites saving something that never\n * actually happened.\n */\n @Input() public AllowSaveAsWorkflow: boolean = true;\n\n /** Starts collapsed. A plan card sits inline in a conversation and must not dominate it. */\n @Input() public Expanded: boolean = false;\n\n @Output() public SaveAsWorkflowRequested = new EventEmitter<SaveAsWorkflowRequestedEventArgs>();\n @Output() public ExpandedChange = new EventEmitter<boolean>();\n\n public get StepCount(): number {\n return this.Spec?.tasks?.length ?? 0;\n }\n\n public get TempIds(): string[] {\n return (this.Spec?.tasks ?? []).map((t) => t.tempId);\n }\n\n /** One line of progress, or the plan's own reasoning when nothing is running yet. */\n public get SummaryLine(): string {\n if (this.RuntimeStatus) return SummarizeRuntime(this.RuntimeStatus, this.TempIds);\n return this.Spec?.reasoning || `${this.StepCount} step${this.StepCount === 1 ? '' : 's'}`;\n }\n\n /** True once every step has reached a state nothing will move it out of. */\n public get IsFinished(): boolean {\n return !!this.RuntimeStatus && IsRuntimeSettled(this.RuntimeStatus, this.TempIds);\n }\n\n /**\n * Save is offered only once the work has settled — or when there is no runtime at all, which is\n * the design-time case (a plan being previewed before it runs).\n */\n public get CanSave(): boolean {\n return this.AllowSaveAsWorkflow && !!this.Spec && (!this.RuntimeStatus || this.IsFinished);\n }\n\n public ToggleExpanded(): void {\n this.Expanded = !this.Expanded;\n this.ExpandedChange.emit(this.Expanded);\n }\n\n public RequestSave(): void {\n if (this.Spec && this.CanSave) {\n this.SaveAsWorkflowRequested.emit(new SaveAsWorkflowRequestedEventArgs(this.Spec, this.ParentTaskID));\n }\n }\n}\n","@if (Spec) {\n <div class=\"mj-wpc\" [class.mj-wpc--expanded]=\"Expanded\">\n <button type=\"button\" class=\"mj-wpc__header\" (click)=\"ToggleExpanded()\"\n [attr.aria-expanded]=\"Expanded\">\n <i class=\"fa-solid\" [class.fa-chevron-right]=\"!Expanded\" [class.fa-chevron-down]=\"Expanded\"></i>\n <span class=\"mj-wpc__title\">{{ Spec.workflowName }}</span>\n <span class=\"mj-wpc__summary\">{{ SummaryLine }}</span>\n @if (IsFinished) {\n <i class=\"fa-solid fa-circle-check mj-wpc__done\" aria-label=\"Finished\"></i>\n }\n </button>\n\n @if (Expanded) {\n <div class=\"mj-wpc__body\">\n <!-- The SAME component the editor uses, read-only. A simpler chat-only renderer would be a\n second thing that can disagree with the canvas about what a graph means. -->\n <mj-task-graph-editor\n class=\"mj-wpc__canvas\"\n [Spec]=\"Spec\"\n [RuntimeStatus]=\"RuntimeStatus\"\n [ReadOnly]=\"true\"\n [ShowPalette]=\"false\"\n [ShowToolbar]=\"false\"\n [ShowMinimap]=\"false\">\n </mj-task-graph-editor>\n\n @if (CanSave) {\n <div class=\"mj-wpc__actions\">\n <button mjButton variant=\"primary\" size=\"sm\" (click)=\"RequestSave()\">\n <i class=\"fa-solid fa-floppy-disk\"></i> Save this approach as a Workflow\n </button>\n </div>\n }\n </div>\n }\n </div>\n}\n"]}
|
|
@@ -34,6 +34,14 @@ export declare class TasksFullViewComponent extends BaseAngularComponent impleme
|
|
|
34
34
|
onTaskClick(task: MJTaskEntity): Promise<void>;
|
|
35
35
|
private loadTaskHierarchy;
|
|
36
36
|
private loadTaskDependencies;
|
|
37
|
+
/**
|
|
38
|
+
* Builds the TaskID -> AgentRunID map from each task's own AgentRunID column.
|
|
39
|
+
*
|
|
40
|
+
* Previously this joined tasks to runs through the shared ConversationDetailID, which meant
|
|
41
|
+
* every sibling task in a graph resolved to the SAME agent run — the run link in the Gantt
|
|
42
|
+
* and detail panel was wrong for all but one task. Each task now records the specific run
|
|
43
|
+
* that executed it (Task.AgentRunID), so the mapping is direct and needs no query at all.
|
|
44
|
+
*/
|
|
37
45
|
private loadAgentRuns;
|
|
38
46
|
backToTaskList(): void;
|
|
39
47
|
onSubTaskClick(subTask: MJTaskEntity): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-full-view.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/task/tasks-full-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAW,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAsB,MAAM,+BAA+B,CAAC;;AAKzG;;;;GAIG;AACH,qBA2Ha,sBAAuB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAClF,aAAa,EAAG,MAAM,CAAC;IACvB,WAAW,EAAG,QAAQ,CAAC;IACvB,UAAU,EAAE,MAAM,CAAS;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;oBAAkC,MAAM;kBAAY,MAAM;OAAM;IAChF,YAAY,8BAAqC;IAEpD,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,aAAa,EAAE,YAAY,EAAE,CAAM;IACnC,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,kBAAkB,EAAE,YAAY,EAAE,CAAM;IACxC,gBAAgB,EAAE,sBAAsB,EAAE,CAAM;IAChD,WAAW,sBAA6B;IACxC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,mBAAmB,EAAE,OAAO,CAAS;IACrC,SAAS,EAAE,OAAO,CAAS;IAClC,OAAO,CAAC,kBAAkB,CAAkB;IAE5C,QAAQ;IAIR,WAAW,CAAC,OAAO,EAAE,aAAa;IAerB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA4D1B,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAY7C,iBAAiB;YAgDjB,oBAAoB;
|
|
1
|
+
{"version":3,"file":"tasks-full-view.component.d.ts","sourceRoot":"","sources":["../../../../src/lib/components/task/tasks-full-view.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AACzG,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,OAAO,EAAE,QAAQ,EAAW,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAsB,MAAM,+BAA+B,CAAC;;AAKzG;;;;GAIG;AACH,qBA2Ha,sBAAuB,SAAQ,oBAAqB,YAAW,MAAM,EAAE,SAAS;IAClF,aAAa,EAAG,MAAM,CAAC;IACvB,WAAW,EAAG,QAAQ,CAAC;IACvB,UAAU,EAAE,MAAM,CAAS;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB;oBAAkC,MAAM;kBAAY,MAAM;OAAM;IAChF,YAAY,8BAAqC;IAEpD,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,aAAa,EAAE,YAAY,EAAE,CAAM;IACnC,QAAQ,EAAE,YAAY,EAAE,CAAM;IAC9B,kBAAkB,EAAE,YAAY,EAAE,CAAM;IACxC,gBAAgB,EAAE,sBAAsB,EAAE,CAAM;IAChD,WAAW,sBAA6B;IACxC,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IACzC,mBAAmB,EAAE,OAAO,CAAS;IACrC,SAAS,EAAE,OAAO,CAAS;IAClC,OAAO,CAAC,kBAAkB,CAAkB;IAE5C,QAAQ;IAIR,WAAW,CAAC,OAAO,EAAE,aAAa;IAerB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IA4D1B,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAY7C,iBAAiB;YAgDjB,oBAAoB;IAkClC;;;;;;;OAOG;IACH,OAAO,CAAC,aAAa;IASd,cAAc,IAAI,IAAI;IAYtB,cAAc,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI;IAK3C,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAKzE,cAAc,IAAI,MAAM;IAMxB,wBAAwB,IAAI,MAAM;yCA7O9B,sBAAsB;2CAAtB,sBAAsB;CAqPlC"}
|
|
@@ -164,7 +164,7 @@ export class TasksFullViewComponent extends BaseAngularComponent {
|
|
|
164
164
|
// Load task dependencies for this hierarchy
|
|
165
165
|
await this.loadTaskDependencies(rootId);
|
|
166
166
|
// Load agent runs for this hierarchy
|
|
167
|
-
|
|
167
|
+
this.loadAgentRuns(allHierarchy);
|
|
168
168
|
}
|
|
169
169
|
else {
|
|
170
170
|
console.error('❌ Failed to load task hierarchy:', hierarchyResult.ErrorMessage);
|
|
@@ -208,69 +208,21 @@ export class TasksFullViewComponent extends BaseAngularComponent {
|
|
|
208
208
|
this.taskDependencies = [];
|
|
209
209
|
}
|
|
210
210
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
const schema = '__mj';
|
|
225
|
-
// Build filter to find agent runs for these conversation details
|
|
226
|
-
// Use a subquery to avoid passing large ID lists
|
|
227
|
-
const taskIds = tasks.map(t => `'${t.ID}'`).join(',');
|
|
228
|
-
const agentRunsResult = await rv.RunView({
|
|
229
|
-
EntityName: 'MJ: AI Agent Runs',
|
|
230
|
-
ExtraFilter: `
|
|
231
|
-
ConversationDetailID IN (
|
|
232
|
-
SELECT DISTINCT ConversationDetailID
|
|
233
|
-
FROM [${schema}].[vwTasks]
|
|
234
|
-
WHERE ID IN (${taskIds})
|
|
235
|
-
AND ConversationDetailID IS NOT NULL
|
|
236
|
-
)
|
|
237
|
-
`,
|
|
238
|
-
ResultType: 'entity_object'
|
|
239
|
-
}, this.currentUser);
|
|
240
|
-
if (agentRunsResult.Success) {
|
|
241
|
-
const agentRuns = agentRunsResult.Results || [];
|
|
242
|
-
console.log(`🤖 Loaded ${agentRuns.length} agent runs`, agentRuns);
|
|
243
|
-
// Build map: ConversationDetailID -> AgentRunID
|
|
244
|
-
const convoToRunMap = new Map();
|
|
245
|
-
agentRuns.forEach(run => {
|
|
246
|
-
if (run.ConversationDetailID) {
|
|
247
|
-
convoToRunMap.set(run.ConversationDetailID, run.ID);
|
|
248
|
-
console.log(`📝 Mapping ConvoDetailID ${run.ConversationDetailID} -> RunID ${run.ID}`);
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
// Map TaskID -> AgentRunID using ConversationDetailID as the link
|
|
252
|
-
tasks.forEach(task => {
|
|
253
|
-
console.log(`🔍 Task ${task.Name} - ConvoDetailID: ${task.ConversationDetailID}, AgentID: ${task.AgentID}`);
|
|
254
|
-
if (task.ConversationDetailID) {
|
|
255
|
-
const agentRunId = convoToRunMap.get(task.ConversationDetailID);
|
|
256
|
-
if (agentRunId) {
|
|
257
|
-
this.agentRunMap.set(task.ID, agentRunId);
|
|
258
|
-
console.log(`✅ Mapped Task ${task.ID} -> AgentRun ${agentRunId}`);
|
|
259
|
-
}
|
|
260
|
-
else {
|
|
261
|
-
console.log(`⚠️ No agent run found for ConvoDetailID ${task.ConversationDetailID}`);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
});
|
|
265
|
-
console.log(`🔗 Mapped ${this.agentRunMap.size} tasks to agent runs`, Array.from(this.agentRunMap.entries()));
|
|
266
|
-
}
|
|
267
|
-
else {
|
|
268
|
-
console.error('❌ Failed to load agent runs:', agentRunsResult.ErrorMessage);
|
|
211
|
+
/**
|
|
212
|
+
* Builds the TaskID -> AgentRunID map from each task's own AgentRunID column.
|
|
213
|
+
*
|
|
214
|
+
* Previously this joined tasks to runs through the shared ConversationDetailID, which meant
|
|
215
|
+
* every sibling task in a graph resolved to the SAME agent run — the run link in the Gantt
|
|
216
|
+
* and detail panel was wrong for all but one task. Each task now records the specific run
|
|
217
|
+
* that executed it (Task.AgentRunID), so the mapping is direct and needs no query at all.
|
|
218
|
+
*/
|
|
219
|
+
loadAgentRuns(tasks) {
|
|
220
|
+
this.agentRunMap.clear();
|
|
221
|
+
for (const task of tasks) {
|
|
222
|
+
if (task.AgentRunID) {
|
|
223
|
+
this.agentRunMap.set(task.ID, task.AgentRunID);
|
|
269
224
|
}
|
|
270
225
|
}
|
|
271
|
-
catch (error) {
|
|
272
|
-
console.error('Failed to load agent runs:', error);
|
|
273
|
-
}
|
|
274
226
|
}
|
|
275
227
|
backToTaskList() {
|
|
276
228
|
this.selectedTask = null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tasks-full-view.component.js","sourceRoot":"","sources":["../../../../src/lib/components/task/tasks-full-view.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\nimport { UserInfo, RunView } from '@memberjunction/core';\nimport { MJTaskEntity, MJTaskDependencyEntity, MJAIAgentRunEntity } from '@memberjunction/core-entities';\nimport { TaskComponent } from '@memberjunction/ng-tasks';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Full-page tasks view with task list and Gantt chart\n * Generic component that displays tasks based on provided filter\n * Supports drilling into individual tasks to see sub-tasks\n */\n@Component({\n selector: 'mj-tasks-full-view',\n standalone: true,\n imports: [TaskComponent],\n template: `\n <div class=\"tasks-full-view\">\n @if (!selectedTask) {\n <!-- Task List View -->\n <mj-task\n [tasks]=\"filteredTasks\"\n [title]=\"'Tasks'\"\n [description]=\"getDescription()\"\n [showHeader]=\"true\"\n [showViewToggle]=\"false\"\n [viewMode]=\"'simple'\"\n (taskClicked)=\"onTaskClick($event)\">\n </mj-task>\n } @else {\n <!-- Task Detail View with Sub-tasks -->\n <div class=\"task-detail-view\" [class.swoosh-in]=\"showDetailAnimation\">\n <!-- Breadcrumb -->\n <div class=\"breadcrumb-nav\">\n <button class=\"breadcrumb-back\" (click)=\"backToTaskList()\">\n <i class=\"fas fa-arrow-left\"></i>\n <span>Back to Tasks</span>\n </button>\n <div class=\"breadcrumb-divider\">/</div>\n <span class=\"breadcrumb-current\">{{ selectedTask.Name }}</span>\n </div>\n\n <!-- Task Details & Sub-tasks with Gantt Toggle -->\n <mj-task\n [tasks]=\"subTasks\"\n [ganttTasks]=\"subTasksWithParent\"\n [taskDependencies]=\"taskDependencies\"\n [agentRunMap]=\"agentRunMap\"\n [title]=\"selectedTask.Name\"\n [description]=\"getTaskDetailDescription()\"\n [showHeader]=\"true\"\n [showViewToggle]=\"true\"\n [viewMode]=\"'gantt'\"\n (taskClicked)=\"onSubTaskClick($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task>\n </div>\n }\n </div>\n `,\n styles: [`\n .tasks-full-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n\n .task-detail-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .swoosh-in {\n animation: swooshIn 0.3s ease-out;\n }\n\n @keyframes swooshIn {\n from {\n opacity: 0;\n transform: translateX(50px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n }\n\n .breadcrumb-nav {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .breadcrumb-back {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: transparent;\n border: 1px solid var(--mj-border-strong);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .breadcrumb-back:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-text-disabled);\n }\n\n .breadcrumb-back i {\n font-size: 12px;\n }\n\n .breadcrumb-divider {\n color: var(--mj-text-disabled);\n font-size: 14px;\n }\n\n .breadcrumb-current {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n `]\n})\nexport class TasksFullViewComponent extends BaseAngularComponent implements OnInit, OnChanges {\n @Input() environmentId!: string;\n @Input() currentUser!: UserInfo;\n @Input() baseFilter: string = '1=1'; // SQL filter for tasks (default: show all)\n @Input() activeTaskId?: string; // Task ID to auto-select and drill into\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n @Output() taskSelected = new EventEmitter<string | null>(); // Emits task ID when drill-down occurs, null when returning to list\n\n public allTasks: MJTaskEntity[] = [];\n public filteredTasks: MJTaskEntity[] = [];\n public subTasks: MJTaskEntity[] = [];\n public subTasksWithParent: MJTaskEntity[] = []; // Includes parent for Gantt hierarchy\n public taskDependencies: MJTaskDependencyEntity[] = []; // Dependencies for Gantt links\n public agentRunMap = new Map<string, string>(); // Maps TaskID -> AgentRunID\n public selectedTask: MJTaskEntity | null = null;\n public showDetailAnimation: boolean = false;\n public isLoading: boolean = false;\n private aiEngineConfigured: boolean = false;\n\n ngOnInit() {\n this.loadTasks();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n // Reload tasks if baseFilter changes\n if (changes['baseFilter'] && !changes['baseFilter'].firstChange) {\n this.loadTasks();\n }\n\n // Auto-drill into task if activeTaskId changes\n if (changes['activeTaskId'] && this.activeTaskId) {\n const task = this.allTasks.find(t => UUIDsEqual(t.ID, this.activeTaskId));\n if (task) {\n this.onTaskClick(task);\n }\n }\n }\n\n public async loadTasks(): Promise<void> {\n this.isLoading = true;\n\n try {\n // Configure AIEngineBase on first load (false = don't force refresh)\n if (!this.aiEngineConfigured) {\n await AIEngineBase.Instance.Config(false);\n this.aiEngineConfigured = true;\n }\n\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n console.log('📝 Tasks filter SQL:', this.baseFilter);\n\n // Load all tasks with the provided filter\n const tasksResult = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: this.baseFilter,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 1000,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n console.log('📊 Tasks query result:', {\n success: tasksResult.Success,\n resultCount: tasksResult.Results?.length || 0,\n errorMessage: tasksResult.ErrorMessage\n });\n\n if (tasksResult.Success) {\n this.allTasks = tasksResult.Results || [];\n this.filteredTasks = this.allTasks;\n console.log(`📋 Loaded ${this.allTasks.length} tasks`);\n if (this.allTasks.length === 0) {\n console.log('💡 No tasks found with current filter');\n } else {\n console.log('✅ Sample task:', {\n id: this.allTasks[0].ID,\n name: this.allTasks[0].Name,\n status: this.allTasks[0].Status,\n conversationDetailID: this.allTasks[0].ConversationDetailID\n });\n }\n } else {\n console.error('❌ Failed to load tasks:', tasksResult.ErrorMessage);\n this.allTasks = [];\n this.filteredTasks = [];\n }\n } catch (error) {\n console.error('Failed to load tasks:', error);\n this.allTasks = [];\n this.filteredTasks = [];\n } finally {\n this.isLoading = false;\n }\n }\n\n public async onTaskClick(task: MJTaskEntity): Promise<void> {\n console.log('Task clicked:', task);\n this.selectedTask = task;\n this.showDetailAnimation = true;\n\n // Emit task selection event for URL tracking\n this.taskSelected.emit(task.ID);\n\n // Load all tasks in the hierarchy using RootParentID\n await this.loadTaskHierarchy(task);\n }\n\n private async loadTaskHierarchy(task: MJTaskEntity): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n // Use RootParentID to load all tasks in this hierarchy\n // If task has no RootParentID, it's the root itself, so use its ID\n const rootId = task.RootParentID || task.ID;\n\n // Load all tasks where RootParentID matches, or tasks that are the root itself\n const hierarchyResult = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: `RootParentID='${rootId}' OR ID='${rootId}'`,\n OrderBy: '__mj_CreatedAt ASC',\n MaxRows: 1000,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (hierarchyResult.Success) {\n const allHierarchy = hierarchyResult.Results || [];\n\n // For list view: Filter out the clicked task itself - only show its children/descendants\n this.subTasks = allHierarchy.filter(t => !UUIDsEqual(t.ID, task.ID));\n\n // For Gantt view: Include the parent task so hierarchy works correctly\n this.subTasksWithParent = allHierarchy;\n\n console.log(`📋 Loaded ${this.subTasks.length} tasks in hierarchy for root ${rootId}`);\n\n // Load task dependencies for this hierarchy\n await this.loadTaskDependencies(rootId);\n\n // Load agent runs for this hierarchy\n await this.loadAgentRuns(allHierarchy);\n } else {\n console.error('❌ Failed to load task hierarchy:', hierarchyResult.ErrorMessage);\n this.subTasks = [];\n this.subTasksWithParent = [];\n this.taskDependencies = [];\n }\n } catch (error) {\n console.error('Failed to load task hierarchy:', error);\n this.subTasks = [];\n }\n }\n\n private async loadTaskDependencies(rootId: string): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n // Load task dependencies where either TaskID or DependsOnTaskID is in this hierarchy\n // Use subquery to find all tasks with this RootParentID\n // Note: Using __mj as the default schema - this is the standard MJ schema\n const schema = '__mj';\n const depsResult = await rv.RunView<MJTaskDependencyEntity>(\n {\n EntityName: 'MJ: Task Dependencies',\n ExtraFilter: `\n TaskID IN (SELECT ID FROM [${schema}].[vwTasks] WHERE RootParentID='${rootId}' OR ID='${rootId}')\n OR\n DependsOnTaskID IN (SELECT ID FROM [${schema}].[vwTasks] WHERE RootParentID='${rootId}' OR ID='${rootId}')\n `,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (depsResult.Success) {\n this.taskDependencies = depsResult.Results || [];\n console.log(`🔗 Loaded ${this.taskDependencies.length} task dependencies`);\n } else {\n console.error('❌ Failed to load task dependencies:', depsResult.ErrorMessage);\n this.taskDependencies = [];\n }\n } catch (error) {\n console.error('Failed to load task dependencies:', error);\n this.taskDependencies = [];\n }\n }\n\n private async loadAgentRuns(tasks: MJTaskEntity[]): Promise<void> {\n try {\n // Clear existing map\n this.agentRunMap.clear();\n\n // Get all unique ConversationDetailIDs from tasks (filter out nulls)\n const conversationDetailIds = tasks\n .filter(t => t.ConversationDetailID != null)\n .map(t => t.ConversationDetailID!);\n\n if (conversationDetailIds.length === 0) {\n console.log('💡 No tasks with ConversationDetailID');\n return;\n }\n\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n const schema = '__mj';\n\n // Build filter to find agent runs for these conversation details\n // Use a subquery to avoid passing large ID lists\n const taskIds = tasks.map(t => `'${t.ID}'`).join(',');\n\n const agentRunsResult = await rv.RunView<MJAIAgentRunEntity>(\n {\n EntityName: 'MJ: AI Agent Runs',\n ExtraFilter: `\n ConversationDetailID IN (\n SELECT DISTINCT ConversationDetailID\n FROM [${schema}].[vwTasks]\n WHERE ID IN (${taskIds})\n AND ConversationDetailID IS NOT NULL\n )\n `,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (agentRunsResult.Success) {\n const agentRuns = agentRunsResult.Results || [];\n console.log(`🤖 Loaded ${agentRuns.length} agent runs`, agentRuns);\n\n // Build map: ConversationDetailID -> AgentRunID\n const convoToRunMap = new Map<string, string>();\n agentRuns.forEach(run => {\n if (run.ConversationDetailID) {\n convoToRunMap.set(run.ConversationDetailID, run.ID);\n console.log(`📝 Mapping ConvoDetailID ${run.ConversationDetailID} -> RunID ${run.ID}`);\n }\n });\n\n // Map TaskID -> AgentRunID using ConversationDetailID as the link\n tasks.forEach(task => {\n console.log(`🔍 Task ${task.Name} - ConvoDetailID: ${task.ConversationDetailID}, AgentID: ${task.AgentID}`);\n if (task.ConversationDetailID) {\n const agentRunId = convoToRunMap.get(task.ConversationDetailID);\n if (agentRunId) {\n this.agentRunMap.set(task.ID, agentRunId);\n console.log(`✅ Mapped Task ${task.ID} -> AgentRun ${agentRunId}`);\n } else {\n console.log(`⚠️ No agent run found for ConvoDetailID ${task.ConversationDetailID}`);\n }\n }\n });\n\n console.log(`🔗 Mapped ${this.agentRunMap.size} tasks to agent runs`, Array.from(this.agentRunMap.entries()));\n } else {\n console.error('❌ Failed to load agent runs:', agentRunsResult.ErrorMessage);\n }\n } catch (error) {\n console.error('Failed to load agent runs:', error);\n }\n }\n\n public backToTaskList(): void {\n this.selectedTask = null;\n this.subTasks = [];\n this.subTasksWithParent = [];\n this.taskDependencies = [];\n this.agentRunMap.clear();\n this.showDetailAnimation = false;\n\n // Emit null to indicate returning to task list (for URL tracking)\n this.taskSelected.emit(null);\n }\n\n public onSubTaskClick(subTask: MJTaskEntity): void {\n console.log('Sub-task clicked:', subTask);\n // Could drill down further if needed\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n // Bubble up the event to parent component\n this.openEntityRecord.emit(event);\n }\n\n public getDescription(): string {\n const activeCount = this.allTasks.filter(t => t.Status === 'Pending' || t.Status === 'In Progress').length;\n const completedCount = this.allTasks.filter(t => t.Status === 'Complete').length;\n return `${activeCount} active, ${completedCount} completed, ${this.allTasks.length} total`;\n }\n\n public getTaskDetailDescription(): string {\n if (this.subTasks.length === 0) {\n return 'No sub-tasks';\n }\n const activeCount = this.subTasks.filter(t => t.Status === 'Pending' || t.Status === 'In Progress').length;\n const completedCount = this.subTasks.filter(t => t.Status === 'Complete').length;\n return `${activeCount} active, ${completedCount} completed, ${this.subTasks.length} sub-tasks`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"tasks-full-view.component.js","sourceRoot":"","sources":["../../../../src/lib/components/task/tasks-full-view.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChanges } from '@angular/core';\nimport { BaseAngularComponent } from '@memberjunction/ng-base-types';\n\nimport { UserInfo, RunView } from '@memberjunction/core';\nimport { MJTaskEntity, MJTaskDependencyEntity, MJAIAgentRunEntity } from '@memberjunction/core-entities';\nimport { TaskComponent } from '@memberjunction/ng-tasks';\nimport { AIEngineBase } from '@memberjunction/ai-engine-base';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Full-page tasks view with task list and Gantt chart\n * Generic component that displays tasks based on provided filter\n * Supports drilling into individual tasks to see sub-tasks\n */\n@Component({\n selector: 'mj-tasks-full-view',\n standalone: true,\n imports: [TaskComponent],\n template: `\n <div class=\"tasks-full-view\">\n @if (!selectedTask) {\n <!-- Task List View -->\n <mj-task\n [tasks]=\"filteredTasks\"\n [title]=\"'Tasks'\"\n [description]=\"getDescription()\"\n [showHeader]=\"true\"\n [showViewToggle]=\"false\"\n [viewMode]=\"'simple'\"\n (taskClicked)=\"onTaskClick($event)\">\n </mj-task>\n } @else {\n <!-- Task Detail View with Sub-tasks -->\n <div class=\"task-detail-view\" [class.swoosh-in]=\"showDetailAnimation\">\n <!-- Breadcrumb -->\n <div class=\"breadcrumb-nav\">\n <button class=\"breadcrumb-back\" (click)=\"backToTaskList()\">\n <i class=\"fas fa-arrow-left\"></i>\n <span>Back to Tasks</span>\n </button>\n <div class=\"breadcrumb-divider\">/</div>\n <span class=\"breadcrumb-current\">{{ selectedTask.Name }}</span>\n </div>\n\n <!-- Task Details & Sub-tasks with Gantt Toggle -->\n <mj-task\n [tasks]=\"subTasks\"\n [ganttTasks]=\"subTasksWithParent\"\n [taskDependencies]=\"taskDependencies\"\n [agentRunMap]=\"agentRunMap\"\n [title]=\"selectedTask.Name\"\n [description]=\"getTaskDetailDescription()\"\n [showHeader]=\"true\"\n [showViewToggle]=\"true\"\n [viewMode]=\"'gantt'\"\n (taskClicked)=\"onSubTaskClick($event)\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task>\n </div>\n }\n </div>\n `,\n styles: [`\n .tasks-full-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n }\n\n .task-detail-view {\n display: flex;\n flex-direction: column;\n height: 100%;\n background: var(--mj-bg-surface);\n }\n\n .swoosh-in {\n animation: swooshIn 0.3s ease-out;\n }\n\n @keyframes swooshIn {\n from {\n opacity: 0;\n transform: translateX(50px);\n }\n to {\n opacity: 1;\n transform: translateX(0);\n }\n }\n\n .breadcrumb-nav {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 16px 24px;\n background: var(--mj-bg-surface);\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .breadcrumb-back {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n background: transparent;\n border: 1px solid var(--mj-border-strong);\n border-radius: 6px;\n color: var(--mj-text-secondary);\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s;\n }\n\n .breadcrumb-back:hover {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-text-disabled);\n }\n\n .breadcrumb-back i {\n font-size: 12px;\n }\n\n .breadcrumb-divider {\n color: var(--mj-text-disabled);\n font-size: 14px;\n }\n\n .breadcrumb-current {\n font-size: 14px;\n font-weight: 600;\n color: var(--mj-text-primary);\n }\n `]\n})\nexport class TasksFullViewComponent extends BaseAngularComponent implements OnInit, OnChanges {\n @Input() environmentId!: string;\n @Input() currentUser!: UserInfo;\n @Input() baseFilter: string = '1=1'; // SQL filter for tasks (default: show all)\n @Input() activeTaskId?: string; // Task ID to auto-select and drill into\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n @Output() taskSelected = new EventEmitter<string | null>(); // Emits task ID when drill-down occurs, null when returning to list\n\n public allTasks: MJTaskEntity[] = [];\n public filteredTasks: MJTaskEntity[] = [];\n public subTasks: MJTaskEntity[] = [];\n public subTasksWithParent: MJTaskEntity[] = []; // Includes parent for Gantt hierarchy\n public taskDependencies: MJTaskDependencyEntity[] = []; // Dependencies for Gantt links\n public agentRunMap = new Map<string, string>(); // Maps TaskID -> AgentRunID\n public selectedTask: MJTaskEntity | null = null;\n public showDetailAnimation: boolean = false;\n public isLoading: boolean = false;\n private aiEngineConfigured: boolean = false;\n\n ngOnInit() {\n this.loadTasks();\n }\n\n ngOnChanges(changes: SimpleChanges) {\n // Reload tasks if baseFilter changes\n if (changes['baseFilter'] && !changes['baseFilter'].firstChange) {\n this.loadTasks();\n }\n\n // Auto-drill into task if activeTaskId changes\n if (changes['activeTaskId'] && this.activeTaskId) {\n const task = this.allTasks.find(t => UUIDsEqual(t.ID, this.activeTaskId));\n if (task) {\n this.onTaskClick(task);\n }\n }\n }\n\n public async loadTasks(): Promise<void> {\n this.isLoading = true;\n\n try {\n // Configure AIEngineBase on first load (false = don't force refresh)\n if (!this.aiEngineConfigured) {\n await AIEngineBase.Instance.Config(false);\n this.aiEngineConfigured = true;\n }\n\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n console.log('📝 Tasks filter SQL:', this.baseFilter);\n\n // Load all tasks with the provided filter\n const tasksResult = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: this.baseFilter,\n OrderBy: '__mj_CreatedAt DESC',\n MaxRows: 1000,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n console.log('📊 Tasks query result:', {\n success: tasksResult.Success,\n resultCount: tasksResult.Results?.length || 0,\n errorMessage: tasksResult.ErrorMessage\n });\n\n if (tasksResult.Success) {\n this.allTasks = tasksResult.Results || [];\n this.filteredTasks = this.allTasks;\n console.log(`📋 Loaded ${this.allTasks.length} tasks`);\n if (this.allTasks.length === 0) {\n console.log('💡 No tasks found with current filter');\n } else {\n console.log('✅ Sample task:', {\n id: this.allTasks[0].ID,\n name: this.allTasks[0].Name,\n status: this.allTasks[0].Status,\n conversationDetailID: this.allTasks[0].ConversationDetailID\n });\n }\n } else {\n console.error('❌ Failed to load tasks:', tasksResult.ErrorMessage);\n this.allTasks = [];\n this.filteredTasks = [];\n }\n } catch (error) {\n console.error('Failed to load tasks:', error);\n this.allTasks = [];\n this.filteredTasks = [];\n } finally {\n this.isLoading = false;\n }\n }\n\n public async onTaskClick(task: MJTaskEntity): Promise<void> {\n console.log('Task clicked:', task);\n this.selectedTask = task;\n this.showDetailAnimation = true;\n\n // Emit task selection event for URL tracking\n this.taskSelected.emit(task.ID);\n\n // Load all tasks in the hierarchy using RootParentID\n await this.loadTaskHierarchy(task);\n }\n\n private async loadTaskHierarchy(task: MJTaskEntity): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n // Use RootParentID to load all tasks in this hierarchy\n // If task has no RootParentID, it's the root itself, so use its ID\n const rootId = task.RootParentID || task.ID;\n\n // Load all tasks where RootParentID matches, or tasks that are the root itself\n const hierarchyResult = await rv.RunView<MJTaskEntity>(\n {\n EntityName: 'MJ: Tasks',\n ExtraFilter: `RootParentID='${rootId}' OR ID='${rootId}'`,\n OrderBy: '__mj_CreatedAt ASC',\n MaxRows: 1000,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (hierarchyResult.Success) {\n const allHierarchy = hierarchyResult.Results || [];\n\n // For list view: Filter out the clicked task itself - only show its children/descendants\n this.subTasks = allHierarchy.filter(t => !UUIDsEqual(t.ID, task.ID));\n\n // For Gantt view: Include the parent task so hierarchy works correctly\n this.subTasksWithParent = allHierarchy;\n\n console.log(`📋 Loaded ${this.subTasks.length} tasks in hierarchy for root ${rootId}`);\n\n // Load task dependencies for this hierarchy\n await this.loadTaskDependencies(rootId);\n\n // Load agent runs for this hierarchy\n this.loadAgentRuns(allHierarchy);\n } else {\n console.error('❌ Failed to load task hierarchy:', hierarchyResult.ErrorMessage);\n this.subTasks = [];\n this.subTasksWithParent = [];\n this.taskDependencies = [];\n }\n } catch (error) {\n console.error('Failed to load task hierarchy:', error);\n this.subTasks = [];\n }\n }\n\n private async loadTaskDependencies(rootId: string): Promise<void> {\n try {\n const rv = RunView.FromMetadataProvider(this.ProviderToUse);\n\n // Load task dependencies where either TaskID or DependsOnTaskID is in this hierarchy\n // Use subquery to find all tasks with this RootParentID\n // Note: Using __mj as the default schema - this is the standard MJ schema\n const schema = '__mj';\n const depsResult = await rv.RunView<MJTaskDependencyEntity>(\n {\n EntityName: 'MJ: Task Dependencies',\n ExtraFilter: `\n TaskID IN (SELECT ID FROM [${schema}].[vwTasks] WHERE RootParentID='${rootId}' OR ID='${rootId}')\n OR\n DependsOnTaskID IN (SELECT ID FROM [${schema}].[vwTasks] WHERE RootParentID='${rootId}' OR ID='${rootId}')\n `,\n ResultType: 'entity_object'\n },\n this.currentUser\n );\n\n if (depsResult.Success) {\n this.taskDependencies = depsResult.Results || [];\n console.log(`🔗 Loaded ${this.taskDependencies.length} task dependencies`);\n } else {\n console.error('❌ Failed to load task dependencies:', depsResult.ErrorMessage);\n this.taskDependencies = [];\n }\n } catch (error) {\n console.error('Failed to load task dependencies:', error);\n this.taskDependencies = [];\n }\n }\n\n /**\n * Builds the TaskID -> AgentRunID map from each task's own AgentRunID column.\n *\n * Previously this joined tasks to runs through the shared ConversationDetailID, which meant\n * every sibling task in a graph resolved to the SAME agent run — the run link in the Gantt\n * and detail panel was wrong for all but one task. Each task now records the specific run\n * that executed it (Task.AgentRunID), so the mapping is direct and needs no query at all.\n */\n private loadAgentRuns(tasks: MJTaskEntity[]): void {\n this.agentRunMap.clear();\n for (const task of tasks) {\n if (task.AgentRunID) {\n this.agentRunMap.set(task.ID, task.AgentRunID);\n }\n }\n }\n\n public backToTaskList(): void {\n this.selectedTask = null;\n this.subTasks = [];\n this.subTasksWithParent = [];\n this.taskDependencies = [];\n this.agentRunMap.clear();\n this.showDetailAnimation = false;\n\n // Emit null to indicate returning to task list (for URL tracking)\n this.taskSelected.emit(null);\n }\n\n public onSubTaskClick(subTask: MJTaskEntity): void {\n console.log('Sub-task clicked:', subTask);\n // Could drill down further if needed\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n // Bubble up the event to parent component\n this.openEntityRecord.emit(event);\n }\n\n public getDescription(): string {\n const activeCount = this.allTasks.filter(t => t.Status === 'Pending' || t.Status === 'In Progress').length;\n const completedCount = this.allTasks.filter(t => t.Status === 'Complete').length;\n return `${activeCount} active, ${completedCount} completed, ${this.allTasks.length} total`;\n }\n\n public getTaskDetailDescription(): string {\n if (this.subTasks.length === 0) {\n return 'No sub-tasks';\n }\n const activeCount = this.subTasks.filter(t => t.Status === 'Pending' || t.Status === 'In Progress').length;\n const completedCount = this.subTasks.filter(t => t.Status === 'Complete').length;\n return `${activeCount} active, ${completedCount} completed, ${this.subTasks.length} sub-tasks`;\n }\n}\n"]}
|