@memberjunction/ng-tasks 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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +75 -0
- package/LICENSE +7 -0
- package/dist/lib/components/simple-task-viewer.component.d.ts.map +1 -1
- package/dist/lib/components/simple-task-viewer.component.js +9 -6
- package/dist/lib/components/simple-task-viewer.component.js.map +1 -1
- package/package.json +21 -18
- package/src/lib/components/simple-task-viewer.component.ts +17 -1
package/.turbo/turbo-build.log
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,80 @@
|
|
|
1
1
|
# @memberjunction/ng-tasks
|
|
2
2
|
|
|
3
|
+
## 6.1.0-edge.1
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 394d276: Phase 1 of the unified workflow DAG engine program (plan: PR #3456) — makes the task substrate tell the truth about what actually happened.
|
|
8
|
+
|
|
9
|
+
**Payloads become columns.** `Task` gains `InputPayload`, `OutputPayload`, `ErrorMessage`, and `AgentRunID`. Inputs and outputs previously rode inside `Task.Description` behind `__TASK_METADATA__` / `__TASK_OUTPUT__` markers, which leaked orchestration plumbing into search results and the task detail panel. A one-time migration backfill converts existing marker rows into the new columns and strips the markers; there is deliberately **no fallback parse** in code, because a fallback with no backfill never dies. The backfill is conservative — a row whose marker text doesn't parse as JSON is left byte-for-byte intact for inspection rather than silently discarded.
|
|
10
|
+
|
|
11
|
+
**Failures propagate instead of stalling.** A `Failed` dependency used to leave its dependents `Pending` forever: they never became eligible, so the graph appeared to finish while work silently never ran — and the parent was marked `Complete` at 100% regardless. Now failure propagates transitively to `Blocked`, and the parent rolls its children up honestly (`Failed` > `Blocked` > `Cancelled` > `Complete`, with progress counting only completed children). Completion notifications fire only for genuinely successful graphs.
|
|
12
|
+
|
|
13
|
+
**Bad graphs are rejected before they are persisted.** Dependency cycles are detected at creation (a cyclic graph could previously be saved and then deadlock silently), and a graph naming an unknown agent is now an error rather than being logged-and-skipped — which used to execute the graph with holes where the caller's tasks should have been.
|
|
14
|
+
|
|
15
|
+
**Waves run in parallel.** Eligible tasks execute with bounded concurrency (5) rather than one at a time, and each pass loads the graph once instead of issuing a dependency query per candidate task. Stalled graphs — pending work, nothing runnable, nothing in flight — are now detected and logged rather than exiting quietly.
|
|
16
|
+
|
|
17
|
+
**The Gantt links the right run.** `Task.AgentRunID` records the specific run that executed each task. The UI previously joined tasks to runs through the shared `ConversationDetailID`, so every sibling task in a graph resolved to the _same_ agent run; the link was wrong for all but one. `Blocked` and `Failed` also now render distinctly instead of inheriting the pending treatment.
|
|
18
|
+
|
|
19
|
+
**New pure graph algorithms** in `@memberjunction/ai-core-plus` (`computeEligibleTasks`, `computeTasksToBlock`, `computeParentRollup`, `detectCycle`, `isGraphStalled`, `findUnknownDependencyRefs`) — dependency-free, operating on plain shapes rather than entities, with 44 unit tests. Phase 2's durable dispatcher consumes these unchanged rather than reimplementing eligibility and propagation.
|
|
20
|
+
|
|
21
|
+
**Also:** dispatcher claim columns (`ClaimedBy`, `ClaimExpiresAt`) and their supporting indexes land now so Phase 2 adds the dispatcher without further schema churn — nothing reads them yet. `AIAgentRunStep.StepType` gains `TaskGraph`. New deterministic integration bundle `task-graph-orchestration` (TG1–TG4) covering cycle rejection, unknown-agent rejection, payload columns, and the new schema's presence in generated metadata.
|
|
22
|
+
|
|
23
|
+
### Patch Changes
|
|
24
|
+
|
|
25
|
+
- 394d276: Declare @angular/\* peer dependencies as ranges (^21.1.3) instead of exact pins across all Angular library packages. Peer declarations are compatibility claims, not install instructions: the exact pins falsely claimed incompatibility with every other Angular 21.x build, produced 502 peer-resolution errors under strict pnpm workspaces, and structurally blocked Angular security patches behind a full republish. Installed versions remain pinned by consuming apps and the era platform manifest; dependencies/devDependencies keep their exact pins.
|
|
26
|
+
- Updated dependencies [394d276]
|
|
27
|
+
- Updated dependencies [394d276]
|
|
28
|
+
- Updated dependencies [394d276]
|
|
29
|
+
- Updated dependencies [394d276]
|
|
30
|
+
- Updated dependencies [394d276]
|
|
31
|
+
- Updated dependencies [394d276]
|
|
32
|
+
- Updated dependencies [394d276]
|
|
33
|
+
- Updated dependencies [394d276]
|
|
34
|
+
- Updated dependencies [394d276]
|
|
35
|
+
- Updated dependencies [394d276]
|
|
36
|
+
- Updated dependencies [394d276]
|
|
37
|
+
- Updated dependencies [394d276]
|
|
38
|
+
- Updated dependencies [394d276]
|
|
39
|
+
- Updated dependencies [394d276]
|
|
40
|
+
- Updated dependencies [394d276]
|
|
41
|
+
- @memberjunction/core@6.1.0-edge.1
|
|
42
|
+
- @memberjunction/core-entities@6.1.0-edge.1
|
|
43
|
+
- @memberjunction/ng-shared-generic@6.1.0-edge.1
|
|
44
|
+
- @memberjunction/ai-core-plus@6.1.0-edge.1
|
|
45
|
+
- @memberjunction/ai-engine-base@6.1.0-edge.1
|
|
46
|
+
- @memberjunction/global@6.1.0-edge.1
|
|
47
|
+
|
|
48
|
+
## 6.1.0-edge.0
|
|
49
|
+
|
|
50
|
+
### Patch Changes
|
|
51
|
+
|
|
52
|
+
- Updated dependencies [2412415]
|
|
53
|
+
- Updated dependencies [9699d0e]
|
|
54
|
+
- Updated dependencies [052b4c7]
|
|
55
|
+
- Updated dependencies [9a905e8]
|
|
56
|
+
- Updated dependencies [841e6ea]
|
|
57
|
+
- Updated dependencies [1d88e00]
|
|
58
|
+
- Updated dependencies [27e4d09]
|
|
59
|
+
- @memberjunction/core-entities@6.1.0-edge.0
|
|
60
|
+
- @memberjunction/core@6.1.0-edge.0
|
|
61
|
+
- @memberjunction/ai-engine-base@6.1.0-edge.0
|
|
62
|
+
- @memberjunction/ai-core-plus@6.1.0-edge.0
|
|
63
|
+
- @memberjunction/ng-shared-generic@6.1.0-edge.0
|
|
64
|
+
- @memberjunction/global@6.1.0-edge.0
|
|
65
|
+
|
|
66
|
+
## 6.0.0
|
|
67
|
+
|
|
68
|
+
### Patch Changes
|
|
69
|
+
|
|
70
|
+
- Updated dependencies [a2670a9]
|
|
71
|
+
- @memberjunction/core@6.0.0
|
|
72
|
+
- @memberjunction/ai-engine-base@6.0.0
|
|
73
|
+
- @memberjunction/ai-core-plus@6.0.0
|
|
74
|
+
- @memberjunction/ng-shared-generic@6.0.0
|
|
75
|
+
- @memberjunction/core-entities@6.0.0
|
|
76
|
+
- @memberjunction/global@6.0.0
|
|
77
|
+
|
|
3
78
|
## 5.51.0
|
|
4
79
|
|
|
5
80
|
### Patch Changes
|
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 MemberJunction
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-task-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/simple-task-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAgB,MAAM,eAAe,CAAC;AAEhG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;;AAI7D;;GAEG;AACH,
|
|
1
|
+
{"version":3,"file":"simple-task-viewer.component.d.ts","sourceRoot":"","sources":["../../../src/lib/components/simple-task-viewer.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,SAAS,EAAgB,MAAM,eAAe,CAAC;AAEhG,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;;AAI7D;;GAEG;AACH,qBAgTa,yBAA0B,YAAW,SAAS;IAChD,KAAK,EAAE,YAAY,EAAE,CAAM;IAC3B,WAAW,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,WAAW,6BAAoC;IAC/C,gBAAgB;oBAAkC,MAAM;kBAAY,MAAM;OAAM;IAEnF,YAAY,EAAE,YAAY,GAAG,IAAI,CAAQ;IAEzC,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO;IAG3C,gBAAgB,EAAE,MAAM,CAAO;IAEtC,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,gBAAgB,CAAK;IAE7B,WAAW;IAIJ,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI;IAKrC,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,GAAG,IAAI;IAIhD,gBAAgB,IAAI,IAAI;IAIxB,kBAAkB,CAAC,KAAK,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IAIzE,WAAW,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAQ3C,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;IAWrC,UAAU,IAAI,IAAI;IAIX,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAWrC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI,GAAG,MAAM;yCAxEjC,yBAAyB;2CAAzB,yBAAyB;CA6ErC"}
|
|
@@ -65,7 +65,7 @@ function SimpleTaskViewerComponent_For_4_Template(rf, ctx) { if (rf & 1) {
|
|
|
65
65
|
const ctx_r2 = i0.ɵɵnextContext();
|
|
66
66
|
i0.ɵɵclassProp("completed", task_r2.Status === "Complete")("selected", ctx_r2.IsTaskSelected(task_r2));
|
|
67
67
|
i0.ɵɵadvance();
|
|
68
|
-
i0.ɵɵclassProp("complete", task_r2.Status === "Complete");
|
|
68
|
+
i0.ɵɵclassProp("complete", task_r2.Status === "Complete")("failed", task_r2.Status === "Failed")("blocked", task_r2.Status === "Blocked");
|
|
69
69
|
i0.ɵɵadvance();
|
|
70
70
|
i0.ɵɵproperty("ngClass", ctx_r2.getStatusIcon(task_r2.Status));
|
|
71
71
|
i0.ɵɵadvance(4);
|
|
@@ -175,7 +175,7 @@ export class SimpleTaskViewerComponent {
|
|
|
175
175
|
i0.ɵɵlistener("mousemove", function SimpleTaskViewerComponent_mousemove_HostBindingHandler($event) { return ctx.handleResize($event); }, i0.ɵɵresolveDocument)("mouseup", function SimpleTaskViewerComponent_mouseup_HostBindingHandler() { return ctx.stopResize(); }, i0.ɵɵresolveDocument);
|
|
176
176
|
} }, inputs: { tasks: "tasks", agentRunMap: "agentRunMap" }, outputs: { taskClicked: "taskClicked", openEntityRecord: "openEntityRecord" }, features: [i0.ɵɵNgOnChangesFeature], decls: 8, vars: 5, consts: [[1, "simple-task-viewer"], [1, "list-layout"], [1, "task-list"], [1, "task-item", 3, "completed", "selected"], [1, "no-tasks"], [1, "list-resizer"], [1, "task-detail-panel", 3, "width"], [1, "task-item", 3, "click"], [1, "status-icon"], [1, "fas", 3, "ngClass"], [1, "task-content"], [1, "task-header"], [1, "task-title-row"], [1, "task-title"], [1, "fas", "fa-check", "completed-check"], [1, "task-progress-compact"], [1, "task-meta"], [1, "due-date"], [1, "assigned-to"], [1, "progress-bar-compact"], [1, "progress-fill-compact"], [1, "progress-text-compact"], [1, "far", "fa-calendar"], [1, "far", "fa-user"], [1, "fas", "fa-tasks"], [1, "list-resizer", 3, "mousedown"], [1, "task-detail-panel"], [3, "closePanel", "openEntityRecord", "task", "agentRunId"]], template: function SimpleTaskViewerComponent_Template(rf, ctx) { if (rf & 1) {
|
|
177
177
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
|
|
178
|
-
i0.ɵɵrepeaterCreate(3, SimpleTaskViewerComponent_For_4_Template, 13,
|
|
178
|
+
i0.ɵɵrepeaterCreate(3, SimpleTaskViewerComponent_For_4_Template, 13, 18, "div", 3, i0.ɵɵrepeaterTrackByIdentity);
|
|
179
179
|
i0.ɵɵconditionalCreate(5, SimpleTaskViewerComponent_Conditional_5_Template, 4, 0, "div", 4);
|
|
180
180
|
i0.ɵɵelementEnd();
|
|
181
181
|
i0.ɵɵconditionalCreate(6, SimpleTaskViewerComponent_Conditional_6_Template, 1, 0, "div", 5);
|
|
@@ -192,7 +192,7 @@ export class SimpleTaskViewerComponent {
|
|
|
192
192
|
i0.ɵɵconditional(ctx.selectedTask ? 6 : -1);
|
|
193
193
|
i0.ɵɵadvance();
|
|
194
194
|
i0.ɵɵconditional(ctx.selectedTask ? 7 : -1);
|
|
195
|
-
} }, dependencies: [CommonModule, i1.NgClass, TaskDetailPanelComponent], styles: [".simple-task-viewer[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail[_ngcontent-%COMP%] {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer[_ngcontent-%COMP%] {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel[_ngcontent-%COMP%] {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .task-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .task-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 4px;\n }\n\n .task-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact[_ngcontent-%COMP%] {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks[_ngcontent-%COMP%] {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }"] });
|
|
195
|
+
} }, dependencies: [CommonModule, i1.NgClass, TaskDetailPanelComponent], styles: [".simple-task-viewer[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout[_ngcontent-%COMP%] {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail[_ngcontent-%COMP%] {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer[_ngcontent-%COMP%] {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer[_ngcontent-%COMP%]:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel[_ngcontent-%COMP%] {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item[_ngcontent-%COMP%] {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item[_ngcontent-%COMP%]:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected[_ngcontent-%COMP%] {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed[_ngcontent-%COMP%] {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon[_ngcontent-%COMP%] {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n \n\n\n\n .status-icon.failed[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n .status-icon.blocked[_ngcontent-%COMP%] {\n background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n }\n\n .task-content[_ngcontent-%COMP%] {\n flex: 1;\n min-width: 0;\n }\n\n .task-header[_ngcontent-%COMP%] {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title[_ngcontent-%COMP%] {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text[_ngcontent-%COMP%] {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check[_ngcontent-%COMP%] {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta[_ngcontent-%COMP%] {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n margin-right: 4px;\n }\n\n .task-description[_ngcontent-%COMP%] {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact[_ngcontent-%COMP%] {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact[_ngcontent-%COMP%] {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact[_ngcontent-%COMP%] {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete[_ngcontent-%COMP%] {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact[_ngcontent-%COMP%] {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks[_ngcontent-%COMP%] {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks[_ngcontent-%COMP%] i[_ngcontent-%COMP%] {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks[_ngcontent-%COMP%] p[_ngcontent-%COMP%] {\n margin: 0;\n font-size: 14px;\n }"] });
|
|
196
196
|
}
|
|
197
197
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(SimpleTaskViewerComponent, [{
|
|
198
198
|
type: Component,
|
|
@@ -207,7 +207,10 @@ export class SimpleTaskViewerComponent {
|
|
|
207
207
|
[class.selected]="IsTaskSelected(task)"
|
|
208
208
|
(click)="onTaskClick(task)">
|
|
209
209
|
<!-- Status Icon -->
|
|
210
|
-
<div class="status-icon"
|
|
210
|
+
<div class="status-icon"
|
|
211
|
+
[class.complete]="task.Status === 'Complete'"
|
|
212
|
+
[class.failed]="task.Status === 'Failed'"
|
|
213
|
+
[class.blocked]="task.Status === 'Blocked'">
|
|
211
214
|
<i class="fas" [ngClass]="getStatusIcon(task.Status)"></i>
|
|
212
215
|
</div>
|
|
213
216
|
<!-- Task Content -->
|
|
@@ -276,7 +279,7 @@ export class SimpleTaskViewerComponent {
|
|
|
276
279
|
}
|
|
277
280
|
</div>
|
|
278
281
|
</div>
|
|
279
|
-
`, styles: ["\n .simple-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .task-content {\n flex: 1;\n min-width: 0;\n }\n\n .task-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta i {\n margin-right: 4px;\n }\n\n .task-description {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n "] }]
|
|
282
|
+
`, styles: ["\n .simple-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n /* Failed and Blocked are distinct outcomes: Failed is this task's own error, Blocked\n means an upstream dependency can never be satisfied. Both previously rendered with\n the default (pending) treatment, so a dead graph looked like a waiting one. */\n .status-icon.failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n .status-icon.blocked {\n background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n }\n\n .task-content {\n flex: 1;\n min-width: 0;\n }\n\n .task-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta i {\n margin-right: 4px;\n }\n\n .task-description {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n "] }]
|
|
280
283
|
}], null, { tasks: [{
|
|
281
284
|
type: Input
|
|
282
285
|
}], agentRunMap: [{
|
|
@@ -292,5 +295,5 @@ export class SimpleTaskViewerComponent {
|
|
|
292
295
|
type: HostListener,
|
|
293
296
|
args: ['document:mouseup']
|
|
294
297
|
}] }); })();
|
|
295
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SimpleTaskViewerComponent, { className: "SimpleTaskViewerComponent", filePath: "src/lib/components/simple-task-viewer.component.ts", lineNumber:
|
|
298
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(SimpleTaskViewerComponent, { className: "SimpleTaskViewerComponent", filePath: "src/lib/components/simple-task-viewer.component.ts", lineNumber: 314 }); })();
|
|
296
299
|
//# sourceMappingURL=simple-task-viewer.component.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"simple-task-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/simple-task-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJTaskEntity } from '@memberjunction/core-entities';\nimport { TaskDetailPanelComponent } from './task-detail-panel.component';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Simple list view for tasks\n */\n@Component({\n selector: 'mj-simple-task-viewer',\n standalone: true,\n imports: [CommonModule, TaskDetailPanelComponent],\n template: `\n <div class=\"simple-task-viewer\">\n <div class=\"list-layout\">\n <div class=\"task-list\" [class.with-detail]=\"selectedTask\">\n @for (task of tasks; track task) {\n <div\n class=\"task-item\"\n [class.completed]=\"task.Status === 'Complete'\"\n [class.selected]=\"IsTaskSelected(task)\"\n (click)=\"onTaskClick(task)\">\n <!-- Status Icon -->\n <div class=\"status-icon\" [class.complete]=\"task.Status === 'Complete'\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon(task.Status)\"></i>\n </div>\n <!-- Task Content -->\n <div class=\"task-content\">\n <div class=\"task-header\">\n <div class=\"task-title-row\">\n <span class=\"task-title\" [class.completed-text]=\"task.Status === 'Complete'\">\n {{ task.Name }}\n @if (task.Status === 'Complete') {\n <i class=\"fas fa-check completed-check\"></i>\n }\n </span>\n <!-- Compact progress indicator for all tasks -->\n @if (task.PercentComplete != null) {\n <div class=\"task-progress-compact\">\n <div class=\"progress-bar-compact\">\n <div class=\"progress-fill-compact\"\n [style.width.%]=\"task.PercentComplete\"\n [class.complete]=\"task.Status === 'Complete'\"></div>\n </div>\n <span class=\"progress-text-compact\">{{ task.PercentComplete }}%</span>\n </div>\n }\n </div>\n <span class=\"task-meta\">\n @if (task.DueAt) {\n <span class=\"due-date\">\n <i class=\"far fa-calendar\"></i>\n {{ formatDate(task.DueAt) }}\n </span>\n }\n @if (task.User) {\n <span class=\"assigned-to\">\n <i class=\"far fa-user\"></i>\n {{ task.User }}\n </span>\n }\n </span>\n </div>\n </div>\n </div>\n }\n \n @if (!tasks || tasks.length === 0) {\n <div class=\"no-tasks\">\n <i class=\"fas fa-tasks\"></i>\n <p>No tasks to display</p>\n </div>\n }\n </div>\n \n @if (selectedTask) {\n <div class=\"list-resizer\"\n (mousedown)=\"startResize($event)\"></div>\n }\n \n @if (selectedTask) {\n <div class=\"task-detail-panel\" [style.width.px]=\"detailPanelWidth\">\n <mj-task-detail-panel\n [task]=\"selectedTask\"\n [agentRunId]=\"getAgentRunId(selectedTask)\"\n (closePanel)=\"closeDetailPanel()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task-detail-panel>\n </div>\n }\n </div>\n </div>\n `,\n styles: [`\n .simple-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n .task-content {\n flex: 1;\n min-width: 0;\n }\n\n .task-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta i {\n margin-right: 4px;\n }\n\n .task-description {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class SimpleTaskViewerComponent implements OnChanges {\n @Input() tasks: MJTaskEntity[] = [];\n @Input() agentRunMap?: Map<string, string>; // Maps TaskID -> AgentRunID\n @Output() taskClicked = new EventEmitter<MJTaskEntity>();\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n\n public selectedTask: MJTaskEntity | null = null;\n\n public IsTaskSelected(task: MJTaskEntity): boolean {\n return UUIDsEqual(this.selectedTask?.ID, task.ID);\n }\n public detailPanelWidth: number = 400;\n\n private isResizing = false;\n private resizeStartX = 0;\n private resizeStartWidth = 0;\n\n ngOnChanges() {\n // Tasks are already loaded\n }\n\n public onTaskClick(task: MJTaskEntity): void {\n this.selectedTask = task;\n this.taskClicked.emit(task);\n }\n\n public getAgentRunId(task: MJTaskEntity): string | null {\n return this.agentRunMap?.get(task.ID) || null;\n }\n\n public closeDetailPanel(): void {\n this.selectedTask = null;\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n this.openEntityRecord.emit(event);\n }\n\n public startResize(event: MouseEvent): void {\n this.isResizing = true;\n this.resizeStartX = event.clientX;\n this.resizeStartWidth = this.detailPanelWidth;\n event.preventDefault();\n }\n\n @HostListener('document:mousemove', ['$event'])\n handleResize(event: MouseEvent): void {\n if (!this.isResizing) return;\n\n const delta = this.resizeStartX - event.clientX;\n const newWidth = this.resizeStartWidth + delta;\n\n // Constrain width between min and max\n this.detailPanelWidth = Math.max(300, Math.min(600, newWidth));\n }\n\n @HostListener('document:mouseup')\n stopResize(): void {\n this.isResizing = false;\n }\n\n public getStatusIcon(status: string): string {\n switch (status) {\n case 'Complete': return 'fa-check-circle';\n case 'In Progress': return 'fa-spinner';\n case 'Pending': return 'fa-clock';\n case 'Blocked': return 'fa-ban';\n case 'Failed': return 'fa-times-circle';\n default: return 'fa-circle';\n }\n }\n\n public formatDate(date: Date | null): string {\n if (!date) return '';\n const d = new Date(date);\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"simple-task-viewer.component.js","sourceRoot":"","sources":["../../../src/lib/components/simple-task-viewer.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, Input, Output, EventEmitter, OnChanges, HostListener } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJTaskEntity } from '@memberjunction/core-entities';\nimport { TaskDetailPanelComponent } from './task-detail-panel.component';\nimport { UUIDsEqual } from '@memberjunction/global';\n\n/**\n * Simple list view for tasks\n */\n@Component({\n selector: 'mj-simple-task-viewer',\n standalone: true,\n imports: [CommonModule, TaskDetailPanelComponent],\n template: `\n <div class=\"simple-task-viewer\">\n <div class=\"list-layout\">\n <div class=\"task-list\" [class.with-detail]=\"selectedTask\">\n @for (task of tasks; track task) {\n <div\n class=\"task-item\"\n [class.completed]=\"task.Status === 'Complete'\"\n [class.selected]=\"IsTaskSelected(task)\"\n (click)=\"onTaskClick(task)\">\n <!-- Status Icon -->\n <div class=\"status-icon\"\n [class.complete]=\"task.Status === 'Complete'\"\n [class.failed]=\"task.Status === 'Failed'\"\n [class.blocked]=\"task.Status === 'Blocked'\">\n <i class=\"fas\" [ngClass]=\"getStatusIcon(task.Status)\"></i>\n </div>\n <!-- Task Content -->\n <div class=\"task-content\">\n <div class=\"task-header\">\n <div class=\"task-title-row\">\n <span class=\"task-title\" [class.completed-text]=\"task.Status === 'Complete'\">\n {{ task.Name }}\n @if (task.Status === 'Complete') {\n <i class=\"fas fa-check completed-check\"></i>\n }\n </span>\n <!-- Compact progress indicator for all tasks -->\n @if (task.PercentComplete != null) {\n <div class=\"task-progress-compact\">\n <div class=\"progress-bar-compact\">\n <div class=\"progress-fill-compact\"\n [style.width.%]=\"task.PercentComplete\"\n [class.complete]=\"task.Status === 'Complete'\"></div>\n </div>\n <span class=\"progress-text-compact\">{{ task.PercentComplete }}%</span>\n </div>\n }\n </div>\n <span class=\"task-meta\">\n @if (task.DueAt) {\n <span class=\"due-date\">\n <i class=\"far fa-calendar\"></i>\n {{ formatDate(task.DueAt) }}\n </span>\n }\n @if (task.User) {\n <span class=\"assigned-to\">\n <i class=\"far fa-user\"></i>\n {{ task.User }}\n </span>\n }\n </span>\n </div>\n </div>\n </div>\n }\n \n @if (!tasks || tasks.length === 0) {\n <div class=\"no-tasks\">\n <i class=\"fas fa-tasks\"></i>\n <p>No tasks to display</p>\n </div>\n }\n </div>\n \n @if (selectedTask) {\n <div class=\"list-resizer\"\n (mousedown)=\"startResize($event)\"></div>\n }\n \n @if (selectedTask) {\n <div class=\"task-detail-panel\" [style.width.px]=\"detailPanelWidth\">\n <mj-task-detail-panel\n [task]=\"selectedTask\"\n [agentRunId]=\"getAgentRunId(selectedTask)\"\n (closePanel)=\"closeDetailPanel()\"\n (openEntityRecord)=\"onOpenEntityRecord($event)\">\n </mj-task-detail-panel>\n </div>\n }\n </div>\n </div>\n `,\n styles: [`\n .simple-task-viewer {\n height: 100%;\n background: var(--mj-bg-surface-sunken);\n overflow: hidden;\n display: flex;\n flex-direction: column;\n }\n\n .list-layout {\n display: flex;\n height: 100%;\n position: relative;\n }\n\n .task-list {\n flex: 1;\n min-width: 400px;\n padding: 16px;\n overflow-y: auto;\n }\n\n .task-list.with-detail {\n border-right: 1px solid var(--mj-border-default);\n }\n\n .list-resizer {\n width: 4px;\n background: var(--mj-border-default);\n cursor: col-resize;\n flex-shrink: 0;\n transition: background 0.2s;\n }\n\n .list-resizer:hover {\n background: var(--mj-brand-primary);\n }\n\n .task-detail-panel {\n min-width: 300px;\n max-width: 600px;\n height: 100%;\n flex-shrink: 0;\n }\n\n .task-item {\n display: flex;\n align-items: flex-start;\n gap: 12px;\n padding: 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n margin-bottom: 8px;\n transition: all 0.2s;\n cursor: pointer;\n }\n\n .task-item:hover {\n border-color: var(--mj-brand-primary);\n box-shadow: var(--mj-shadow-sm);\n transform: translateY(-1px);\n }\n\n .task-item.selected {\n border-color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n box-shadow: var(--mj-shadow-sm);\n }\n\n .task-item.completed {\n background: var(--mj-bg-surface-sunken);\n border-color: var(--mj-border-strong);\n }\n\n .status-icon {\n width: 32px;\n height: 32px;\n border-radius: 6px;\n display: flex;\n align-items: center;\n justify-content: center;\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n flex-shrink: 0;\n }\n\n .status-icon.complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n }\n\n /* Failed and Blocked are distinct outcomes: Failed is this task's own error, Blocked\n means an upstream dependency can never be satisfied. Both previously rendered with\n the default (pending) treatment, so a dead graph looked like a waiting one. */\n .status-icon.failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n }\n\n .status-icon.blocked {\n background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));\n color: var(--mj-text-muted);\n }\n\n .task-content {\n flex: 1;\n min-width: 0;\n }\n\n .task-header {\n display: flex;\n justify-content: space-between;\n align-items: flex-start;\n gap: 12px;\n margin-bottom: 6px;\n }\n\n .task-title-row {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n }\n\n .task-title {\n font-weight: 600;\n color: var(--mj-text-primary);\n font-size: 14px;\n display: flex;\n align-items: center;\n gap: 6px;\n }\n\n .task-title.completed-text {\n color: var(--mj-text-muted);\n text-decoration: line-through;\n text-decoration-thickness: 1.5px;\n }\n\n .completed-check {\n color: var(--mj-status-success);\n font-size: 12px;\n }\n\n .task-meta {\n display: flex;\n gap: 12px;\n font-size: 12px;\n color: var(--mj-text-muted);\n white-space: nowrap;\n }\n\n .task-meta i {\n margin-right: 4px;\n }\n\n .task-description {\n font-size: 13px;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n line-height: 1.5;\n }\n\n .task-progress-compact {\n display: flex;\n align-items: center;\n gap: 6px;\n flex-shrink: 0;\n }\n\n .progress-bar-compact {\n width: 80px;\n height: 4px;\n background: var(--mj-border-default);\n border-radius: 2px;\n overflow: hidden;\n }\n\n .progress-fill-compact {\n height: 100%;\n background: var(--mj-brand-primary);\n transition: width 0.3s ease;\n }\n\n .progress-fill-compact.complete {\n background: var(--mj-status-success);\n }\n\n .progress-text-compact {\n font-size: 10px;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 30px;\n text-align: right;\n }\n\n .no-tasks {\n text-align: center;\n padding: 60px 20px;\n color: var(--mj-text-disabled);\n }\n\n .no-tasks i {\n font-size: 48px;\n opacity: 0.3;\n margin-bottom: 12px;\n }\n\n .no-tasks p {\n margin: 0;\n font-size: 14px;\n }\n `]\n})\nexport class SimpleTaskViewerComponent implements OnChanges {\n @Input() tasks: MJTaskEntity[] = [];\n @Input() agentRunMap?: Map<string, string>; // Maps TaskID -> AgentRunID\n @Output() taskClicked = new EventEmitter<MJTaskEntity>();\n @Output() openEntityRecord = new EventEmitter<{ entityName: string; recordId: string }>();\n\n public selectedTask: MJTaskEntity | null = null;\n\n public IsTaskSelected(task: MJTaskEntity): boolean {\n return UUIDsEqual(this.selectedTask?.ID, task.ID);\n }\n public detailPanelWidth: number = 400;\n\n private isResizing = false;\n private resizeStartX = 0;\n private resizeStartWidth = 0;\n\n ngOnChanges() {\n // Tasks are already loaded\n }\n\n public onTaskClick(task: MJTaskEntity): void {\n this.selectedTask = task;\n this.taskClicked.emit(task);\n }\n\n public getAgentRunId(task: MJTaskEntity): string | null {\n return this.agentRunMap?.get(task.ID) || null;\n }\n\n public closeDetailPanel(): void {\n this.selectedTask = null;\n }\n\n public onOpenEntityRecord(event: { entityName: string; recordId: string }): void {\n this.openEntityRecord.emit(event);\n }\n\n public startResize(event: MouseEvent): void {\n this.isResizing = true;\n this.resizeStartX = event.clientX;\n this.resizeStartWidth = this.detailPanelWidth;\n event.preventDefault();\n }\n\n @HostListener('document:mousemove', ['$event'])\n handleResize(event: MouseEvent): void {\n if (!this.isResizing) return;\n\n const delta = this.resizeStartX - event.clientX;\n const newWidth = this.resizeStartWidth + delta;\n\n // Constrain width between min and max\n this.detailPanelWidth = Math.max(300, Math.min(600, newWidth));\n }\n\n @HostListener('document:mouseup')\n stopResize(): void {\n this.isResizing = false;\n }\n\n public getStatusIcon(status: string): string {\n switch (status) {\n case 'Complete': return 'fa-check-circle';\n case 'In Progress': return 'fa-spinner';\n case 'Pending': return 'fa-clock';\n case 'Blocked': return 'fa-ban';\n case 'Failed': return 'fa-times-circle';\n default: return 'fa-circle';\n }\n }\n\n public formatDate(date: Date | null): string {\n if (!date) return '';\n const d = new Date(date);\n return d.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });\n }\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,32 +1,26 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memberjunction/ng-tasks",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "6.1.0-edge.1",
|
|
4
4
|
"description": "MemberJunction: Angular components for task visualization and management with Gantt chart support",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "ngc",
|
|
9
|
-
"watch": "ngc --watch",
|
|
10
|
-
"test": "vitest run",
|
|
11
|
-
"test:watch": "vitest"
|
|
12
|
-
},
|
|
13
7
|
"dependencies": {
|
|
14
8
|
"@angular/animations": "21.1.3",
|
|
15
9
|
"@angular/common": "21.1.3",
|
|
16
10
|
"@angular/core": "21.1.3",
|
|
17
11
|
"@angular/platform-browser": "21.1.3",
|
|
18
|
-
"@memberjunction/ai-core-plus": "
|
|
19
|
-
"@memberjunction/ai-engine-base": "
|
|
20
|
-
"@memberjunction/core": "
|
|
21
|
-
"@memberjunction/core-entities": "
|
|
22
|
-
"@memberjunction/global": "
|
|
23
|
-
"@memberjunction/ng-shared-generic": "
|
|
12
|
+
"@memberjunction/ai-core-plus": "6.1.0-edge.1",
|
|
13
|
+
"@memberjunction/ai-engine-base": "6.1.0-edge.1",
|
|
14
|
+
"@memberjunction/core": "6.1.0-edge.1",
|
|
15
|
+
"@memberjunction/core-entities": "6.1.0-edge.1",
|
|
16
|
+
"@memberjunction/global": "6.1.0-edge.1",
|
|
17
|
+
"@memberjunction/ng-shared-generic": "6.1.0-edge.1",
|
|
24
18
|
"dhtmlx-gantt": "^9.1.1",
|
|
25
19
|
"rxjs": "^7.8.2"
|
|
26
20
|
},
|
|
27
21
|
"peerDependencies": {
|
|
28
|
-
"@angular/common": "21.1.3",
|
|
29
|
-
"@angular/core": "21.1.3"
|
|
22
|
+
"@angular/common": "^21.1.3",
|
|
23
|
+
"@angular/core": "^21.1.3"
|
|
30
24
|
},
|
|
31
25
|
"author": "MemberJunction",
|
|
32
26
|
"license": "MIT",
|
|
@@ -35,7 +29,16 @@
|
|
|
35
29
|
"url": "https://github.com/MemberJunction/MJ"
|
|
36
30
|
},
|
|
37
31
|
"devDependencies": {
|
|
38
|
-
"@memberjunction/ng-test-utils": "
|
|
39
|
-
"vitest": "^4.0.18"
|
|
32
|
+
"@memberjunction/ng-test-utils": "6.1.0-edge.1",
|
|
33
|
+
"vitest": "^4.0.18",
|
|
34
|
+
"@angular/compiler-cli": "21.1.3"
|
|
35
|
+
},
|
|
36
|
+
"mjUILayer": "widgets",
|
|
37
|
+
"scripts": {
|
|
38
|
+
"build": "ngc",
|
|
39
|
+
"watch": "ngc --watch",
|
|
40
|
+
"test": "vitest run",
|
|
41
|
+
"test:types": "tsc --noEmit -p tsconfig.spec.json",
|
|
42
|
+
"test:watch": "vitest"
|
|
40
43
|
}
|
|
41
|
-
}
|
|
44
|
+
}
|
|
@@ -22,7 +22,10 @@ import { UUIDsEqual } from '@memberjunction/global';
|
|
|
22
22
|
[class.selected]="IsTaskSelected(task)"
|
|
23
23
|
(click)="onTaskClick(task)">
|
|
24
24
|
<!-- Status Icon -->
|
|
25
|
-
<div class="status-icon"
|
|
25
|
+
<div class="status-icon"
|
|
26
|
+
[class.complete]="task.Status === 'Complete'"
|
|
27
|
+
[class.failed]="task.Status === 'Failed'"
|
|
28
|
+
[class.blocked]="task.Status === 'Blocked'">
|
|
26
29
|
<i class="fas" [ngClass]="getStatusIcon(task.Status)"></i>
|
|
27
30
|
</div>
|
|
28
31
|
<!-- Task Content -->
|
|
@@ -184,6 +187,19 @@ import { UUIDsEqual } from '@memberjunction/global';
|
|
|
184
187
|
color: var(--mj-status-success);
|
|
185
188
|
}
|
|
186
189
|
|
|
190
|
+
/* Failed and Blocked are distinct outcomes: Failed is this task's own error, Blocked
|
|
191
|
+
means an upstream dependency can never be satisfied. Both previously rendered with
|
|
192
|
+
the default (pending) treatment, so a dead graph looked like a waiting one. */
|
|
193
|
+
.status-icon.failed {
|
|
194
|
+
background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));
|
|
195
|
+
color: var(--mj-status-error);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.status-icon.blocked {
|
|
199
|
+
background: color-mix(in srgb, var(--mj-text-muted) 15%, var(--mj-bg-surface));
|
|
200
|
+
color: var(--mj-text-muted);
|
|
201
|
+
}
|
|
202
|
+
|
|
187
203
|
.task-content {
|
|
188
204
|
flex: 1;
|
|
189
205
|
min-width: 0;
|