@critical-path/svelte 0.13.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -1
- package/README.md +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +150 -4
- package/dist/task-metrics-state.svelte.d.ts +24 -0
- package/dist/task-metrics-state.svelte.d.ts.map +1 -0
- package/package.json +4 -4
- package/src/index.test.ts +76 -1
- package/src/index.ts +1 -0
- package/src/task-metrics-state.svelte.ts +83 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,36 @@
|
|
|
1
1
|
# @critical-path/svelte
|
|
2
2
|
|
|
3
|
+
## 0.14.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9c31e8d: Introduce comprehensive Task Metrics, Inferred Actuals, Progress Inference, Earned Value Management (EVM), and Time-Series Progress History Curve Profiling.
|
|
8
|
+
- **Inferred Actuals**: Automatic start/completion auto-stamping on workflow transitions with fallback inference from activity logs, plus automatic completion timestamp clearing on task re-open.
|
|
9
|
+
- **Reality Delta**: Multi-dimensional variance analysis comparing planned estimates to logged hours (`effortVarianceHours`, `durationVarianceHours`, `accuracyRatio`, `isOverdue`, `isOverEstimate`).
|
|
10
|
+
- **Progress Inference**: Deterministic priority waterfall resolving completion progress across explicit values, checklist/todo completion ratios, logged effort, and elapsed schedule time.
|
|
11
|
+
- **Earned Value Management (EVM)**: Task-level PV, EV, AC, CV, SV, CPI, and SPI calculations.
|
|
12
|
+
- **Time-Series History & Curve Shapes**: Historical timeline reconstruction from activity logs with piecewise interpolation curve classification (`linear`, `s_curve`, `early_surge`, `late_rush`, `stalled`).
|
|
13
|
+
- **Full-Stack Integration**: REST endpoints (`/tasks/:id/metrics`, `/tasks/:id/progress-history`), Client SDK methods, React `useTaskMetrics` hook, Svelte 5 `TaskMetricsState`, and MCP tools (`get_task_metrics`, `get_task_progress_history`).
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Updated dependencies [9c31e8d]
|
|
18
|
+
- @critical-path/core@0.17.0
|
|
19
|
+
- @critical-path/client@0.11.0
|
|
20
|
+
- @critical-path/mcp@0.4.0
|
|
21
|
+
|
|
22
|
+
## 0.13.1
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- Updated dependencies [b4b2c98]
|
|
27
|
+
- @critical-path/mcp@0.3.1
|
|
28
|
+
|
|
3
29
|
## 0.13.0
|
|
4
30
|
|
|
5
31
|
### Minor Changes
|
|
6
32
|
|
|
7
|
-
- e2a7eef: Introduce
|
|
33
|
+
- e2a7eef: Introduce Ladder of Abstraction and Critical Path Method (CPM) timeline synthesis to the framework data model:
|
|
8
34
|
- **Core Domain**: Multi-scale timeline synthesis model across Macro phase rollups, Standard CPM Gantt schedule (early/late bounds, float/slack calculation, bottleneck identification), and Concrete grounding (deliverables, file attachments, daily effort distribution, and reality deltas). Added `calculateCriticalPath`, `getTimelineLadder`, and `getTaskLadder` to `CriticalPathEngine`.
|
|
9
35
|
- **Server Router**: HTTP endpoints `GET /projects/:id/critical-path`, `GET /projects/:id/ladder`, and `GET /tasks/:id/ladder`.
|
|
10
36
|
- **Client SDK**: `CriticalPathClient` methods `calculateCriticalPath`, `getTimelineLadder`, and `getTaskLadder`.
|
package/README.md
CHANGED
|
@@ -138,7 +138,7 @@ Combines threaded comments with inline attachments (`attachment.commentId === co
|
|
|
138
138
|
{/each}
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
### 4.
|
|
141
|
+
### 4. Ladder of Abstraction (`TimelineLadderState`)
|
|
142
142
|
|
|
143
143
|
Fluidly traverse between Macro phase health, Standard Gantt tasks with CPM critical paths, and Concrete deliverables/effort using Svelte 5 Runes:
|
|
144
144
|
|
package/dist/index.d.ts
CHANGED
|
@@ -13,4 +13,5 @@ export * from './task-transitions-state.svelte.js';
|
|
|
13
13
|
export * from './webmcp-state.svelte.js';
|
|
14
14
|
export * from './critical-path-state.svelte.js';
|
|
15
15
|
export * from './timeline-ladder-state.svelte.js';
|
|
16
|
+
export * from './task-metrics-state.svelte.js';
|
|
16
17
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE/E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,aAAa,GAAG,kBAAkB,CAEnF;AAED,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE/E,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,aAAa,GAAG,kBAAkB,CAEnF;AAED,cAAc,2BAA2B,CAAC;AAC1C,cAAc,wBAAwB,CAAC;AACvC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uCAAuC,CAAC;AACtD,cAAc,0BAA0B,CAAC;AACzC,cAAc,oCAAoC,CAAC;AACnD,cAAc,0BAA0B,CAAC;AACzC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,gCAAgC,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -4004,7 +4004,7 @@ var $ = [
|
|
|
4004
4004
|
{
|
|
4005
4005
|
name: "get_timeline_ladder",
|
|
4006
4006
|
title: "Get Timeline Ladder of Abstraction",
|
|
4007
|
-
description: "Retrieve
|
|
4007
|
+
description: "Retrieve Ladder of Abstraction for a project timeline across macro phase envelopes, standard Gantt tasks & CPM, and concrete deliverables/time/attachments.",
|
|
4008
4008
|
zodSchema: Z({
|
|
4009
4009
|
projectId: X().optional().describe("Project ID (falls back to ambient context if omitted)"),
|
|
4010
4010
|
level: Q([
|
|
@@ -4058,7 +4058,7 @@ var $ = [
|
|
|
4058
4058
|
{
|
|
4059
4059
|
name: "get_task_ladder",
|
|
4060
4060
|
title: "Get Task Ladder View",
|
|
4061
|
-
description: "Retrieve multi-scale
|
|
4061
|
+
description: "Retrieve multi-scale ladder view for a single task, connecting its macro phase, standard CPM timeline position, and concrete work evidence.",
|
|
4062
4062
|
zodSchema: Z({ taskId: X().describe("The task ID") }),
|
|
4063
4063
|
inputSchema: {
|
|
4064
4064
|
type: "object",
|
|
@@ -4071,6 +4071,40 @@ var $ = [
|
|
|
4071
4071
|
},
|
|
4072
4072
|
annotations: { readOnlyHint: !0 },
|
|
4073
4073
|
execute: async (e, t) => t.getTaskLadder(e.taskId)
|
|
4074
|
+
},
|
|
4075
|
+
{
|
|
4076
|
+
name: "get_task_metrics",
|
|
4077
|
+
title: "Get Task Metrics",
|
|
4078
|
+
description: "Retrieve multi-dimensional task metrics including inferred actuals, active working duration, effort/duration/schedule variances, progress inference, and Earned Value Management (EVM) metrics.",
|
|
4079
|
+
zodSchema: Z({ taskId: X().describe("The task ID") }),
|
|
4080
|
+
inputSchema: {
|
|
4081
|
+
type: "object",
|
|
4082
|
+
properties: { taskId: {
|
|
4083
|
+
type: "string",
|
|
4084
|
+
description: "The task ID"
|
|
4085
|
+
} },
|
|
4086
|
+
required: ["taskId"],
|
|
4087
|
+
additionalProperties: !1
|
|
4088
|
+
},
|
|
4089
|
+
annotations: { readOnlyHint: !0 },
|
|
4090
|
+
execute: async (e, t) => t.getTaskMetrics(e.taskId)
|
|
4091
|
+
},
|
|
4092
|
+
{
|
|
4093
|
+
name: "get_task_progress_history",
|
|
4094
|
+
title: "Get Task Progress History",
|
|
4095
|
+
description: "Retrieve time-series progress data points and curve shape classification (linear, s_curve, early_surge, late_rush, stalled) for a task.",
|
|
4096
|
+
zodSchema: Z({ taskId: X().describe("The task ID") }),
|
|
4097
|
+
inputSchema: {
|
|
4098
|
+
type: "object",
|
|
4099
|
+
properties: { taskId: {
|
|
4100
|
+
type: "string",
|
|
4101
|
+
description: "The task ID"
|
|
4102
|
+
} },
|
|
4103
|
+
required: ["taskId"],
|
|
4104
|
+
additionalProperties: !1
|
|
4105
|
+
},
|
|
4106
|
+
annotations: { readOnlyHint: !0 },
|
|
4107
|
+
execute: async (e, t) => t.getTaskProgressHistory(e.taskId)
|
|
4074
4108
|
}
|
|
4075
4109
|
];
|
|
4076
4110
|
new Map($.map((e) => [e.name, e]));
|
|
@@ -4330,9 +4364,121 @@ function vt(e, t, n) {
|
|
|
4330
4364
|
return new _t(e, t, n);
|
|
4331
4365
|
}
|
|
4332
4366
|
//#endregion
|
|
4367
|
+
//#region src/task-metrics-state.svelte.ts
|
|
4368
|
+
var yt = class {
|
|
4369
|
+
client;
|
|
4370
|
+
taskId;
|
|
4371
|
+
#e = t.state(null);
|
|
4372
|
+
get metrics() {
|
|
4373
|
+
return t.get(this.#e);
|
|
4374
|
+
}
|
|
4375
|
+
set metrics(e) {
|
|
4376
|
+
t.set(this.#e, e, !0);
|
|
4377
|
+
}
|
|
4378
|
+
#t = t.state(null);
|
|
4379
|
+
get history() {
|
|
4380
|
+
return t.get(this.#t);
|
|
4381
|
+
}
|
|
4382
|
+
set history(e) {
|
|
4383
|
+
t.set(this.#t, e, !0);
|
|
4384
|
+
}
|
|
4385
|
+
#n = t.state(!1);
|
|
4386
|
+
get loading() {
|
|
4387
|
+
return t.get(this.#n);
|
|
4388
|
+
}
|
|
4389
|
+
set loading(e) {
|
|
4390
|
+
t.set(this.#n, e, !0);
|
|
4391
|
+
}
|
|
4392
|
+
#r = t.state(null);
|
|
4393
|
+
get error() {
|
|
4394
|
+
return t.get(this.#r);
|
|
4395
|
+
}
|
|
4396
|
+
set error(e) {
|
|
4397
|
+
t.set(this.#r, e, !0);
|
|
4398
|
+
}
|
|
4399
|
+
#i = t.derived(() => this.metrics?.inferredActuals ?? null);
|
|
4400
|
+
get inferredActuals() {
|
|
4401
|
+
return t.get(this.#i);
|
|
4402
|
+
}
|
|
4403
|
+
set inferredActuals(e) {
|
|
4404
|
+
t.set(this.#i, e);
|
|
4405
|
+
}
|
|
4406
|
+
#a = t.derived(() => this.metrics?.realityDelta ?? null);
|
|
4407
|
+
get realityDelta() {
|
|
4408
|
+
return t.get(this.#a);
|
|
4409
|
+
}
|
|
4410
|
+
set realityDelta(e) {
|
|
4411
|
+
t.set(this.#a, e);
|
|
4412
|
+
}
|
|
4413
|
+
#o = t.derived(() => this.metrics?.progress ?? null);
|
|
4414
|
+
get progress() {
|
|
4415
|
+
return t.get(this.#o);
|
|
4416
|
+
}
|
|
4417
|
+
set progress(e) {
|
|
4418
|
+
t.set(this.#o, e);
|
|
4419
|
+
}
|
|
4420
|
+
#s = t.derived(() => this.metrics?.evm ?? null);
|
|
4421
|
+
get evm() {
|
|
4422
|
+
return t.get(this.#s);
|
|
4423
|
+
}
|
|
4424
|
+
set evm(e) {
|
|
4425
|
+
t.set(this.#s, e);
|
|
4426
|
+
}
|
|
4427
|
+
#c = t.derived(() => this.history?.curveProfile ?? null);
|
|
4428
|
+
get curveProfile() {
|
|
4429
|
+
return t.get(this.#c);
|
|
4430
|
+
}
|
|
4431
|
+
set curveProfile(e) {
|
|
4432
|
+
t.set(this.#c, e);
|
|
4433
|
+
}
|
|
4434
|
+
constructor(e, t) {
|
|
4435
|
+
this.client = e, this.taskId = t;
|
|
4436
|
+
}
|
|
4437
|
+
async fetchMetrics() {
|
|
4438
|
+
this.loading = !0, this.error = null;
|
|
4439
|
+
try {
|
|
4440
|
+
return this.metrics = await this.client.getTaskMetrics(this.taskId), this.metrics;
|
|
4441
|
+
} catch (e) {
|
|
4442
|
+
return this.error = e instanceof Error ? e : Error(String(e)), null;
|
|
4443
|
+
} finally {
|
|
4444
|
+
this.loading = !1;
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
async fetchHistory() {
|
|
4448
|
+
this.loading = !0, this.error = null;
|
|
4449
|
+
try {
|
|
4450
|
+
return this.history = await this.client.getTaskProgressHistory(this.taskId), this.history;
|
|
4451
|
+
} catch (e) {
|
|
4452
|
+
return this.error = e instanceof Error ? e : Error(String(e)), null;
|
|
4453
|
+
} finally {
|
|
4454
|
+
this.loading = !1;
|
|
4455
|
+
}
|
|
4456
|
+
}
|
|
4457
|
+
async fetchAll() {
|
|
4458
|
+
this.loading = !0, this.error = null;
|
|
4459
|
+
try {
|
|
4460
|
+
let [e, t] = await Promise.all([this.client.getTaskMetrics(this.taskId), this.client.getTaskProgressHistory(this.taskId)]);
|
|
4461
|
+
return this.metrics = e, this.history = t, {
|
|
4462
|
+
metrics: e,
|
|
4463
|
+
history: t
|
|
4464
|
+
};
|
|
4465
|
+
} catch (e) {
|
|
4466
|
+
return this.error = e instanceof Error ? e : Error(String(e)), {
|
|
4467
|
+
metrics: null,
|
|
4468
|
+
history: null
|
|
4469
|
+
};
|
|
4470
|
+
} finally {
|
|
4471
|
+
this.loading = !1;
|
|
4472
|
+
}
|
|
4473
|
+
}
|
|
4474
|
+
};
|
|
4475
|
+
function bt(e, t) {
|
|
4476
|
+
return new yt(e, t);
|
|
4477
|
+
}
|
|
4478
|
+
//#endregion
|
|
4333
4479
|
//#region src/index.ts
|
|
4334
|
-
function
|
|
4480
|
+
function xt(t) {
|
|
4335
4481
|
return new e(t);
|
|
4336
4482
|
}
|
|
4337
4483
|
//#endregion
|
|
4338
|
-
export { ne as AttachmentState, ee as CommentState, ht as CriticalPathState, oe as DeliverableState, ce as DeliverableSummaryState, ue as KanbanState, r as ProjectState, ie as TaskActivityState, a as TaskState, fe as TaskTransitionsState, _t as TimelineLadderState, pt as WebMcpState, s as WorkflowState, re as createAttachmentState, te as createCommentState,
|
|
4484
|
+
export { ne as AttachmentState, ee as CommentState, ht as CriticalPathState, oe as DeliverableState, ce as DeliverableSummaryState, ue as KanbanState, r as ProjectState, ie as TaskActivityState, yt as TaskMetricsState, a as TaskState, fe as TaskTransitionsState, _t as TimelineLadderState, pt as WebMcpState, s as WorkflowState, re as createAttachmentState, te as createCommentState, xt as createCriticalPathClient, gt as createCriticalPathState, se as createDeliverableState, le as createDeliverableSummaryState, de as createKanbanState, i as createProjectState, ae as createTaskActivityState, bt as createTaskMetricsState, o as createTaskState, pe as createTaskTransitionsState, vt as createTimelineLadderState, mt as createWebMcpState, c as createWorkflowState };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { CriticalPathClient } from '@critical-path/client';
|
|
2
|
+
import type { TaskMetrics, TaskProgressHistory, TaskInferredActuals, RealityDelta, TaskProgressInference, TaskEVM, ProgressCurveProfile } from '@critical-path/core';
|
|
3
|
+
export declare class TaskMetricsState {
|
|
4
|
+
private client;
|
|
5
|
+
taskId: string;
|
|
6
|
+
metrics: TaskMetrics | null;
|
|
7
|
+
history: TaskProgressHistory | null;
|
|
8
|
+
loading: boolean;
|
|
9
|
+
error: Error | null;
|
|
10
|
+
inferredActuals: TaskInferredActuals | null;
|
|
11
|
+
realityDelta: RealityDelta | null;
|
|
12
|
+
progress: TaskProgressInference | null;
|
|
13
|
+
evm: TaskEVM | null;
|
|
14
|
+
curveProfile: ProgressCurveProfile | null;
|
|
15
|
+
constructor(client: CriticalPathClient, taskId: string);
|
|
16
|
+
fetchMetrics(): Promise<TaskMetrics | null>;
|
|
17
|
+
fetchHistory(): Promise<TaskProgressHistory | null>;
|
|
18
|
+
fetchAll(): Promise<{
|
|
19
|
+
metrics: TaskMetrics | null;
|
|
20
|
+
history: TaskProgressHistory | null;
|
|
21
|
+
}>;
|
|
22
|
+
}
|
|
23
|
+
export declare function createTaskMetricsState(client: CriticalPathClient, taskId: string): TaskMetricsState;
|
|
24
|
+
//# sourceMappingURL=task-metrics-state.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"task-metrics-state.svelte.d.ts","sourceRoot":"","sources":["../src/task-metrics-state.svelte.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,KAAK,EACV,WAAW,EACX,mBAAmB,EACnB,mBAAmB,EACnB,YAAY,EACZ,qBAAqB,EACrB,OAAO,EACP,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAE7B,qBAAa,gBAAgB;IAazB,OAAO,CAAC,MAAM;IACP,MAAM,EAAE,MAAM;IAbvB,OAAO,qBAAoC;IAC3C,OAAO,6BAA4C;IACnD,OAAO,UAA0B;IACjC,KAAK,eAA8B;IAEnC,eAAe,6BAA+E;IAC9F,YAAY,sBAAqE;IACjF,QAAQ,+BAA0E;IAClF,GAAG,iBAAuD;IAC1D,YAAY,8BAA6E;gBAG/E,MAAM,EAAE,kBAAkB,EAC3B,MAAM,EAAE,MAAM;IAGjB,YAAY,IAAI,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAc3C,YAAY,IAAI,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAcnD,QAAQ,IAAI,OAAO,CAAC;QAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI,CAAA;KAAE,CAAC;CAkBhG;AAED,wBAAgB,sBAAsB,CACpC,MAAM,EAAE,kBAAkB,EAC1B,MAAM,EAAE,MAAM,GACb,gBAAgB,CAElB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@critical-path/svelte",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Svelte 5 Runes state and reactive integrations for Critical Path headless project management framework",
|
|
5
5
|
"author": "Jack James",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,9 +18,9 @@
|
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@critical-path/
|
|
22
|
-
"@critical-path/
|
|
23
|
-
"@critical-path/core": "0.
|
|
21
|
+
"@critical-path/client": "0.11.0",
|
|
22
|
+
"@critical-path/mcp": "0.4.0",
|
|
23
|
+
"@critical-path/core": "0.17.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"svelte": "^5.0.0"
|
package/src/index.test.ts
CHANGED
|
@@ -26,7 +26,9 @@ import {
|
|
|
26
26
|
CriticalPathState,
|
|
27
27
|
createCriticalPathState,
|
|
28
28
|
TimelineLadderState,
|
|
29
|
-
createTimelineLadderState
|
|
29
|
+
createTimelineLadderState,
|
|
30
|
+
TaskMetricsState,
|
|
31
|
+
createTaskMetricsState
|
|
30
32
|
} from './index.js';
|
|
31
33
|
import type { CriticalPathClient } from '@critical-path/client';
|
|
32
34
|
import type {
|
|
@@ -660,6 +662,79 @@ describe('@critical-path/svelte Svelte 5 Runes Test Suite', () => {
|
|
|
660
662
|
expect(mockClient.getTimelineLadder).toHaveBeenCalledWith('proj_ladder', { level: 'macro' });
|
|
661
663
|
});
|
|
662
664
|
});
|
|
665
|
+
|
|
666
|
+
describe('TaskMetricsState', () => {
|
|
667
|
+
it('fetches metrics and progress history and derives subfields', async () => {
|
|
668
|
+
const mockMetrics = {
|
|
669
|
+
taskId: 'task_1',
|
|
670
|
+
inferredActuals: {
|
|
671
|
+
actualStartDate: '2026-09-01T00:00:00Z',
|
|
672
|
+
actualEndDate: '2026-09-05T00:00:00Z',
|
|
673
|
+
isStartDateInferred: false,
|
|
674
|
+
isEndDateInferred: true,
|
|
675
|
+
activeWorkingHours: 96
|
|
676
|
+
},
|
|
677
|
+
realityDelta: {
|
|
678
|
+
estimatedHours: 20,
|
|
679
|
+
loggedHours: 15,
|
|
680
|
+
varianceHours: -5,
|
|
681
|
+
effortVarianceHours: -5,
|
|
682
|
+
accuracyRatio: 0.75,
|
|
683
|
+
isOverdue: false,
|
|
684
|
+
isOverEstimate: false
|
|
685
|
+
},
|
|
686
|
+
progress: {
|
|
687
|
+
progressPercentage: 75,
|
|
688
|
+
source: 'explicit' as const,
|
|
689
|
+
isExplicit: true,
|
|
690
|
+
breakdown: { explicitProgress: 75 }
|
|
691
|
+
},
|
|
692
|
+
evm: {
|
|
693
|
+
plannedValue: 20,
|
|
694
|
+
earnedValue: 15,
|
|
695
|
+
actualCost: 15,
|
|
696
|
+
costVariance: 0,
|
|
697
|
+
scheduleVariance: -5,
|
|
698
|
+
costPerformanceIndex: 1.0,
|
|
699
|
+
schedulePerformanceIndex: 0.75
|
|
700
|
+
}
|
|
701
|
+
};
|
|
702
|
+
|
|
703
|
+
const mockHistory = {
|
|
704
|
+
taskId: 'task_1',
|
|
705
|
+
points: [
|
|
706
|
+
{ timestamp: '2026-09-01T00:00:00Z', progress: 0, status: 'todo', semanticStatus: 'not_started' as const, action: 'task.created' },
|
|
707
|
+
{ timestamp: '2026-09-05T00:00:00Z', progress: 75, status: 'in_progress', semanticStatus: 'in_progress' as const, action: 'task.updated' }
|
|
708
|
+
],
|
|
709
|
+
curveProfile: 'linear' as const
|
|
710
|
+
};
|
|
711
|
+
|
|
712
|
+
const mockClient = {
|
|
713
|
+
getTaskMetrics: vi.fn().mockResolvedValue(mockMetrics),
|
|
714
|
+
getTaskProgressHistory: vi.fn().mockResolvedValue(mockHistory)
|
|
715
|
+
} as unknown as CriticalPathClient;
|
|
716
|
+
|
|
717
|
+
const state = createTaskMetricsState(mockClient, 'task_1');
|
|
718
|
+
expect(state.metrics).toBeNull();
|
|
719
|
+
expect(state.history).toBeNull();
|
|
720
|
+
expect(state.inferredActuals).toBeNull();
|
|
721
|
+
expect(state.realityDelta).toBeNull();
|
|
722
|
+
expect(state.progress).toBeNull();
|
|
723
|
+
expect(state.evm).toBeNull();
|
|
724
|
+
expect(state.curveProfile).toBeNull();
|
|
725
|
+
|
|
726
|
+
await state.fetchAll();
|
|
727
|
+
expect(mockClient.getTaskMetrics).toHaveBeenCalledWith('task_1');
|
|
728
|
+
expect(mockClient.getTaskProgressHistory).toHaveBeenCalledWith('task_1');
|
|
729
|
+
expect(state.metrics).toEqual(mockMetrics);
|
|
730
|
+
expect(state.history).toEqual(mockHistory);
|
|
731
|
+
expect(state.inferredActuals?.activeWorkingHours).toBe(96);
|
|
732
|
+
expect(state.realityDelta?.effortVarianceHours).toBe(-5);
|
|
733
|
+
expect(state.progress?.progressPercentage).toBe(75);
|
|
734
|
+
expect(state.evm?.costPerformanceIndex).toBe(1.0);
|
|
735
|
+
expect(state.curveProfile).toBe('linear');
|
|
736
|
+
});
|
|
737
|
+
});
|
|
663
738
|
});
|
|
664
739
|
|
|
665
740
|
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
/// <reference types="svelte" />
|
|
2
|
+
import type { CriticalPathClient } from '@critical-path/client';
|
|
3
|
+
import type {
|
|
4
|
+
TaskMetrics,
|
|
5
|
+
TaskProgressHistory,
|
|
6
|
+
TaskInferredActuals,
|
|
7
|
+
RealityDelta,
|
|
8
|
+
TaskProgressInference,
|
|
9
|
+
TaskEVM,
|
|
10
|
+
ProgressCurveProfile
|
|
11
|
+
} from '@critical-path/core';
|
|
12
|
+
|
|
13
|
+
export class TaskMetricsState {
|
|
14
|
+
metrics = $state<TaskMetrics | null>(null);
|
|
15
|
+
history = $state<TaskProgressHistory | null>(null);
|
|
16
|
+
loading = $state<boolean>(false);
|
|
17
|
+
error = $state<Error | null>(null);
|
|
18
|
+
|
|
19
|
+
inferredActuals = $derived<TaskInferredActuals | null>(this.metrics?.inferredActuals ?? null);
|
|
20
|
+
realityDelta = $derived<RealityDelta | null>(this.metrics?.realityDelta ?? null);
|
|
21
|
+
progress = $derived<TaskProgressInference | null>(this.metrics?.progress ?? null);
|
|
22
|
+
evm = $derived<TaskEVM | null>(this.metrics?.evm ?? null);
|
|
23
|
+
curveProfile = $derived<ProgressCurveProfile | null>(this.history?.curveProfile ?? null);
|
|
24
|
+
|
|
25
|
+
constructor(
|
|
26
|
+
private client: CriticalPathClient,
|
|
27
|
+
public taskId: string
|
|
28
|
+
) {}
|
|
29
|
+
|
|
30
|
+
async fetchMetrics(): Promise<TaskMetrics | null> {
|
|
31
|
+
this.loading = true;
|
|
32
|
+
this.error = null;
|
|
33
|
+
try {
|
|
34
|
+
this.metrics = await this.client.getTaskMetrics(this.taskId);
|
|
35
|
+
return this.metrics;
|
|
36
|
+
} catch (err) {
|
|
37
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
38
|
+
return null;
|
|
39
|
+
} finally {
|
|
40
|
+
this.loading = false;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async fetchHistory(): Promise<TaskProgressHistory | null> {
|
|
45
|
+
this.loading = true;
|
|
46
|
+
this.error = null;
|
|
47
|
+
try {
|
|
48
|
+
this.history = await this.client.getTaskProgressHistory(this.taskId);
|
|
49
|
+
return this.history;
|
|
50
|
+
} catch (err) {
|
|
51
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
52
|
+
return null;
|
|
53
|
+
} finally {
|
|
54
|
+
this.loading = false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async fetchAll(): Promise<{ metrics: TaskMetrics | null; history: TaskProgressHistory | null }> {
|
|
59
|
+
this.loading = true;
|
|
60
|
+
this.error = null;
|
|
61
|
+
try {
|
|
62
|
+
const [m, h] = await Promise.all([
|
|
63
|
+
this.client.getTaskMetrics(this.taskId),
|
|
64
|
+
this.client.getTaskProgressHistory(this.taskId)
|
|
65
|
+
]);
|
|
66
|
+
this.metrics = m;
|
|
67
|
+
this.history = h;
|
|
68
|
+
return { metrics: m, history: h };
|
|
69
|
+
} catch (err) {
|
|
70
|
+
this.error = err instanceof Error ? err : new Error(String(err));
|
|
71
|
+
return { metrics: null, history: null };
|
|
72
|
+
} finally {
|
|
73
|
+
this.loading = false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function createTaskMetricsState(
|
|
79
|
+
client: CriticalPathClient,
|
|
80
|
+
taskId: string
|
|
81
|
+
): TaskMetricsState {
|
|
82
|
+
return new TaskMetricsState(client, taskId);
|
|
83
|
+
}
|