@planu/cli 0.55.0 → 0.57.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/dist/config/license-plans.json +2 -0
- package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts +7 -1
- package/dist/engine/doc-generator/executive-summary/executive-summary-generator.d.ts.map +1 -1
- package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js +16 -3
- package/dist/engine/doc-generator/executive-summary/executive-summary-generator.js.map +1 -1
- package/dist/engine/doc-generator/executive-summary/html-template.d.ts +5 -2
- package/dist/engine/doc-generator/executive-summary/html-template.d.ts.map +1 -1
- package/dist/engine/doc-generator/executive-summary/html-template.js +7 -3
- package/dist/engine/doc-generator/executive-summary/html-template.js.map +1 -1
- package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts +5 -3
- package/dist/engine/doc-generator/per-spec-report/html-wrapper.d.ts.map +1 -1
- package/dist/engine/doc-generator/per-spec-report/html-wrapper.js +27 -6
- package/dist/engine/doc-generator/per-spec-report/html-wrapper.js.map +1 -1
- package/dist/engine/doc-generator/portal/analytics-page.d.ts +8 -0
- package/dist/engine/doc-generator/portal/analytics-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/analytics-page.js +265 -0
- package/dist/engine/doc-generator/portal/analytics-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/architecture-page.d.ts +6 -0
- package/dist/engine/doc-generator/portal/architecture-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/architecture-page.js +250 -0
- package/dist/engine/doc-generator/portal/architecture-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/changelog-page.d.ts +6 -0
- package/dist/engine/doc-generator/portal/changelog-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/changelog-page.js +204 -0
- package/dist/engine/doc-generator/portal/changelog-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts +11 -0
- package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js +24 -0
- package/dist/engine/doc-generator/portal/data-aggregators/accuracy-aggregator.js.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts +9 -0
- package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js +31 -0
- package/dist/engine/doc-generator/portal/data-aggregators/bottleneck-detector.js.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts +10 -0
- package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js +77 -0
- package/dist/engine/doc-generator/portal/data-aggregators/burndown-aggregator.js.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts +9 -0
- package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js +31 -0
- package/dist/engine/doc-generator/portal/data-aggregators/velocity-aggregator.js.map +1 -0
- package/dist/engine/doc-generator/portal/decision-page.d.ts +6 -0
- package/dist/engine/doc-generator/portal/decision-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/decision-page.js +169 -0
- package/dist/engine/doc-generator/portal/decision-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts +8 -0
- package/dist/engine/doc-generator/portal/decision-timeline-builder.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/decision-timeline-builder.js +131 -0
- package/dist/engine/doc-generator/portal/decision-timeline-builder.js.map +1 -0
- package/dist/engine/doc-generator/portal/index.d.ts +19 -0
- package/dist/engine/doc-generator/portal/index.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/index.js +21 -0
- package/dist/engine/doc-generator/portal/index.js.map +1 -0
- package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts +8 -0
- package/dist/engine/doc-generator/portal/kanban-view-builder.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/kanban-view-builder.js +140 -0
- package/dist/engine/doc-generator/portal/kanban-view-builder.js.map +1 -0
- package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts +27 -0
- package/dist/engine/doc-generator/portal/portal-breadcrumbs.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/portal-breadcrumbs.js +139 -0
- package/dist/engine/doc-generator/portal/portal-breadcrumbs.js.map +1 -0
- package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts +8 -0
- package/dist/engine/doc-generator/portal/portal-landing-cards.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/portal-landing-cards.js +177 -0
- package/dist/engine/doc-generator/portal/portal-landing-cards.js.map +1 -0
- package/dist/engine/doc-generator/portal/portal-navbar.d.ts +15 -0
- package/dist/engine/doc-generator/portal/portal-navbar.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/portal-navbar.js +143 -0
- package/dist/engine/doc-generator/portal/portal-navbar.js.map +1 -0
- package/dist/engine/doc-generator/portal/portal-page-detector.d.ts +22 -0
- package/dist/engine/doc-generator/portal/portal-page-detector.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/portal-page-detector.js +58 -0
- package/dist/engine/doc-generator/portal/portal-page-detector.js.map +1 -0
- package/dist/engine/doc-generator/portal/portal-theme.d.ts +7 -0
- package/dist/engine/doc-generator/portal/portal-theme.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/portal-theme.js +53 -0
- package/dist/engine/doc-generator/portal/portal-theme.js.map +1 -0
- package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts +5 -0
- package/dist/engine/doc-generator/portal/risk-gauge-svg.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/risk-gauge-svg.js +80 -0
- package/dist/engine/doc-generator/portal/risk-gauge-svg.js.map +1 -0
- package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts +11 -0
- package/dist/engine/doc-generator/portal/risk-matrix-svg.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/risk-matrix-svg.js +181 -0
- package/dist/engine/doc-generator/portal/risk-matrix-svg.js.map +1 -0
- package/dist/engine/doc-generator/portal/risk-page.d.ts +7 -0
- package/dist/engine/doc-generator/portal/risk-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/risk-page.js +219 -0
- package/dist/engine/doc-generator/portal/risk-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/roadmap-page.d.ts +7 -0
- package/dist/engine/doc-generator/portal/roadmap-page.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/roadmap-page.js +204 -0
- package/dist/engine/doc-generator/portal/roadmap-page.js.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts +4 -0
- package/dist/engine/doc-generator/portal/svg-charts/bar-chart.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js +50 -0
- package/dist/engine/doc-generator/portal/svg-charts/bar-chart.js.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts +16 -0
- package/dist/engine/doc-generator/portal/svg-charts/chart-utils.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js +45 -0
- package/dist/engine/doc-generator/portal/svg-charts/chart-utils.js.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts +16 -0
- package/dist/engine/doc-generator/portal/svg-charts/line-chart.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/line-chart.js +159 -0
- package/dist/engine/doc-generator/portal/svg-charts/line-chart.js.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts +7 -0
- package/dist/engine/doc-generator/portal/svg-charts/pie-chart.d.ts.map +1 -0
- package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js +52 -0
- package/dist/engine/doc-generator/portal/svg-charts/pie-chart.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts +8 -0
- package/dist/engine/doc-generator/proposal/proposal-gantt-builder.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js +126 -0
- package/dist/engine/doc-generator/proposal/proposal-gantt-builder.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-generator.d.ts +10 -0
- package/dist/engine/doc-generator/proposal/proposal-generator.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-generator.js +93 -0
- package/dist/engine/doc-generator/proposal/proposal-generator.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts +7 -0
- package/dist/engine/doc-generator/proposal/proposal-html-wrapper.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js +31 -0
- package/dist/engine/doc-generator/proposal/proposal-html-wrapper.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts +12 -0
- package/dist/engine/doc-generator/proposal/proposal-i18n.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-i18n.js +230 -0
- package/dist/engine/doc-generator/proposal/proposal-i18n.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts +8 -0
- package/dist/engine/doc-generator/proposal/proposal-kpi-builder.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js +32 -0
- package/dist/engine/doc-generator/proposal/proposal-kpi-builder.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts +14 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js +248 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders-advanced.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts +13 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders.js +423 -0
- package/dist/engine/doc-generator/proposal/proposal-section-builders.js.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-themes.d.ts +4 -0
- package/dist/engine/doc-generator/proposal/proposal-themes.d.ts.map +1 -0
- package/dist/engine/doc-generator/proposal/proposal-themes.js +194 -0
- package/dist/engine/doc-generator/proposal/proposal-themes.js.map +1 -0
- package/dist/engine/pdf/browser-finder.d.ts +5 -0
- package/dist/engine/pdf/browser-finder.d.ts.map +1 -0
- package/dist/engine/pdf/browser-finder.js +119 -0
- package/dist/engine/pdf/browser-finder.js.map +1 -0
- package/dist/engine/pdf/html-to-pdf.d.ts +3 -0
- package/dist/engine/pdf/html-to-pdf.d.ts.map +1 -0
- package/dist/engine/pdf/html-to-pdf.js +114 -0
- package/dist/engine/pdf/html-to-pdf.js.map +1 -0
- package/dist/engine/pdf/index.d.ts +6 -0
- package/dist/engine/pdf/index.d.ts.map +1 -0
- package/dist/engine/pdf/index.js +4 -0
- package/dist/engine/pdf/index.js.map +1 -0
- package/dist/engine/pdf/pdf-options.d.ts +6 -0
- package/dist/engine/pdf/pdf-options.d.ts.map +1 -0
- package/dist/engine/pdf/pdf-options.js +34 -0
- package/dist/engine/pdf/pdf-options.js.map +1 -0
- package/dist/engine/spec-summary-html.d.ts.map +1 -1
- package/dist/engine/spec-summary-html.js +17 -5
- package/dist/engine/spec-summary-html.js.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/tools/export-pdf.d.ts +3 -0
- package/dist/tools/export-pdf.d.ts.map +1 -0
- package/dist/tools/export-pdf.js +82 -0
- package/dist/tools/export-pdf.js.map +1 -0
- package/dist/tools/generate-proposal.d.ts +3 -0
- package/dist/tools/generate-proposal.d.ts.map +1 -0
- package/dist/tools/generate-proposal.js +166 -0
- package/dist/tools/generate-proposal.js.map +1 -0
- package/dist/types/docs.d.ts +41 -0
- package/dist/types/docs.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/pdf.d.ts +42 -0
- package/dist/types/pdf.d.ts.map +1 -0
- package/dist/types/pdf.js +3 -0
- package/dist/types/pdf.js.map +1 -0
- package/dist/types/portal.d.ts +128 -0
- package/dist/types/portal.d.ts.map +1 -0
- package/dist/types/portal.js +3 -0
- package/dist/types/portal.js.map +1 -0
- package/dist/types/proposal.d.ts +57 -0
- package/dist/types/proposal.d.ts.map +1 -0
- package/dist/types/proposal.js +2 -0
- package/dist/types/proposal.js.map +1 -0
- package/package.json +1 -1
- package/src/config/license-plans.json +2 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compute a sprint-level burndown from specs.
|
|
3
|
+
* Uses createdAt to determine when work started and completedAt for done dates.
|
|
4
|
+
* Returns daily data points sorted by date.
|
|
5
|
+
* Returns empty array when no specs have dates.
|
|
6
|
+
*/
|
|
7
|
+
export function computeBurndown(specs) {
|
|
8
|
+
if (specs.length === 0) {
|
|
9
|
+
return [];
|
|
10
|
+
}
|
|
11
|
+
const datedSpecs = specs.filter((s) => Boolean(s.createdAt));
|
|
12
|
+
if (datedSpecs.length === 0) {
|
|
13
|
+
return [];
|
|
14
|
+
}
|
|
15
|
+
const startMs = Math.min(...datedSpecs.map((s) => toMs(s.createdAt)));
|
|
16
|
+
const endMs = computeEndMs(datedSpecs, startMs);
|
|
17
|
+
if (endMs <= startMs) {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
const total = specs.length;
|
|
21
|
+
const spanDays = Math.round((endMs - startMs) / MS_PER_DAY);
|
|
22
|
+
if (spanDays === 0) {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
// Build completion map: date string -> cumulative done count
|
|
26
|
+
const doneByDate = new Map();
|
|
27
|
+
for (const spec of specs) {
|
|
28
|
+
if (spec.status === 'done' && spec.actuals?.completedAt) {
|
|
29
|
+
const dateStr = toDateStr(spec.actuals.completedAt);
|
|
30
|
+
if (dateStr) {
|
|
31
|
+
doneByDate.set(dateStr, (doneByDate.get(dateStr) ?? 0) + 1);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const points = [];
|
|
36
|
+
let cumulativeDone = 0;
|
|
37
|
+
const stepDays = Math.max(1, Math.ceil(spanDays / 20)); // max 20 points
|
|
38
|
+
for (let day = 0; day <= spanDays; day += stepDays) {
|
|
39
|
+
const currentMs = startMs + day * MS_PER_DAY;
|
|
40
|
+
const dateStr = toDateStr(new Date(currentMs).toISOString());
|
|
41
|
+
if (!dateStr) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
// Accumulate done specs up to current date
|
|
45
|
+
for (const [dDate, cnt] of doneByDate) {
|
|
46
|
+
if (dDate <= dateStr) {
|
|
47
|
+
cumulativeDone += cnt;
|
|
48
|
+
doneByDate.delete(dDate); // prevent double-counting
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
const remaining = Math.max(0, total - cumulativeDone);
|
|
52
|
+
const ideal = Math.round(total * (1 - day / spanDays));
|
|
53
|
+
points.push({ date: dateStr, remaining, ideal });
|
|
54
|
+
}
|
|
55
|
+
return points;
|
|
56
|
+
}
|
|
57
|
+
const MS_PER_DAY = 86_400_000;
|
|
58
|
+
function toMs(dateStr) {
|
|
59
|
+
const d = new Date(dateStr);
|
|
60
|
+
return isNaN(d.getTime()) ? 0 : d.getTime();
|
|
61
|
+
}
|
|
62
|
+
function toDateStr(isoStr) {
|
|
63
|
+
const d = new Date(isoStr);
|
|
64
|
+
if (isNaN(d.getTime())) {
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
return d.toISOString().slice(0, 10);
|
|
68
|
+
}
|
|
69
|
+
function computeEndMs(specs, startMs) {
|
|
70
|
+
const completedSpecs = specs.filter((s) => Boolean(s.actuals?.completedAt));
|
|
71
|
+
if (completedSpecs.length > 0) {
|
|
72
|
+
return Math.max(...completedSpecs.map((s) => toMs(s.actuals?.completedAt ?? '')));
|
|
73
|
+
}
|
|
74
|
+
// Default: start + 4 weeks
|
|
75
|
+
return startMs + 28 * MS_PER_DAY;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=burndown-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"burndown-aggregator.js","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/burndown-aggregator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Spec } from '../../../../types/index.js';
|
|
2
|
+
import type { VelocityPeriod } from '../../../../types/portal.js';
|
|
3
|
+
/**
|
|
4
|
+
* Group done specs by the month of their completedAt date.
|
|
5
|
+
* Returns periods sorted chronologically.
|
|
6
|
+
* Specs without actuals or completedAt are ignored.
|
|
7
|
+
*/
|
|
8
|
+
export declare function computeVelocity(specs: Spec[]): VelocityPeriod[];
|
|
9
|
+
//# sourceMappingURL=velocity-aggregator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"velocity-aggregator.d.ts","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/velocity-aggregator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,cAAc,EAAE,CAiB/D"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Group done specs by the month of their completedAt date.
|
|
3
|
+
* Returns periods sorted chronologically.
|
|
4
|
+
* Specs without actuals or completedAt are ignored.
|
|
5
|
+
*/
|
|
6
|
+
export function computeVelocity(specs) {
|
|
7
|
+
const counts = new Map();
|
|
8
|
+
for (const spec of specs) {
|
|
9
|
+
if (spec.status !== 'done' || !spec.actuals?.completedAt) {
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
const period = toYearMonth(spec.actuals.completedAt);
|
|
13
|
+
if (!period) {
|
|
14
|
+
continue;
|
|
15
|
+
}
|
|
16
|
+
counts.set(period, (counts.get(period) ?? 0) + 1);
|
|
17
|
+
}
|
|
18
|
+
return Array.from(counts.entries())
|
|
19
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
20
|
+
.map(([period, count]) => ({ period, count }));
|
|
21
|
+
}
|
|
22
|
+
function toYearMonth(dateStr) {
|
|
23
|
+
const d = new Date(dateStr);
|
|
24
|
+
if (isNaN(d.getTime())) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const y = d.getUTCFullYear();
|
|
28
|
+
const m = String(d.getUTCMonth() + 1).padStart(2, '0');
|
|
29
|
+
return `${y}-${m}`;
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=velocity-aggregator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"velocity-aggregator.js","sourceRoot":"","sources":["../../../../../src/engine/doc-generator/portal/data-aggregators/velocity-aggregator.ts"],"names":[],"mappings":"AAIA;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,KAAa;IAC3C,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEzC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,WAAW,EAAE,CAAC;YACzD,SAAS;QACX,CAAC;QACD,MAAM,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QACrD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,SAAS;QACX,CAAC;QACD,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;SAChC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;SACtC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;AACnD,CAAC;AAED,SAAS,WAAW,CAAC,OAAe;IAClC,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAC5B,IAAI,KAAK,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QACvB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,CAAC,GAAG,CAAC,CAAC,cAAc,EAAE,CAAC;IAC7B,MAAM,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACvD,OAAO,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;AACrB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-page.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-page.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,6BAA6B,CAAC;AAgK9E;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAgClE"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { buildNavbar } from './portal-navbar.js';
|
|
2
|
+
import { getPortalThemeCSS } from './portal-theme.js';
|
|
3
|
+
import { buildDecisionTimeline, DECISION_TIMELINE_CSS } from './decision-timeline-builder.js';
|
|
4
|
+
function escapeHtml(s) {
|
|
5
|
+
return s
|
|
6
|
+
.replace(/&/g, '&')
|
|
7
|
+
.replace(/</g, '<')
|
|
8
|
+
.replace(/>/g, '>')
|
|
9
|
+
.replace(/"/g, '"');
|
|
10
|
+
}
|
|
11
|
+
const CATEGORY_COLORS = {
|
|
12
|
+
architecture: '#7c3aed',
|
|
13
|
+
stack: '#2563eb',
|
|
14
|
+
ux: '#16a34a',
|
|
15
|
+
business: '#ea580c',
|
|
16
|
+
process: '#6b7280',
|
|
17
|
+
};
|
|
18
|
+
function buildPieSlicesSvg(data) {
|
|
19
|
+
const total = data.reduce((s, d) => s + d.value, 0);
|
|
20
|
+
if (total === 0) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
const CX = 80;
|
|
24
|
+
const CY = 80;
|
|
25
|
+
const R = 70;
|
|
26
|
+
const W = 220;
|
|
27
|
+
const H = 160;
|
|
28
|
+
let startAngle = -Math.PI / 2;
|
|
29
|
+
const slices = [];
|
|
30
|
+
const legends = [];
|
|
31
|
+
data.forEach((item, idx) => {
|
|
32
|
+
const sweep = (item.value / total) * 2 * Math.PI;
|
|
33
|
+
const endAngle = startAngle + sweep;
|
|
34
|
+
const x1 = CX + R * Math.cos(startAngle);
|
|
35
|
+
const y1 = CY + R * Math.sin(startAngle);
|
|
36
|
+
const x2 = CX + R * Math.cos(endAngle);
|
|
37
|
+
const y2 = CY + R * Math.sin(endAngle);
|
|
38
|
+
const largeArc = sweep > Math.PI ? 1 : 0;
|
|
39
|
+
const path = `M${CX},${CY} L${x1.toFixed(1)},${y1.toFixed(1)} A${R},${R} 0 ${largeArc},1 ${x2.toFixed(1)},${y2.toFixed(1)} Z`;
|
|
40
|
+
slices.push(`<path d="${path}" fill="${item.color}" stroke="white" stroke-width="2"><title>${escapeHtml(item.label)}: ${item.value}</title></path>`);
|
|
41
|
+
const legendY = 20 + idx * 18;
|
|
42
|
+
legends.push(`<rect x="${W - 100}" y="${legendY - 8}" width="10" height="10" fill="${item.color}"/>` +
|
|
43
|
+
`<text x="${W - 86}" y="${legendY}" font-size="10" fill="#374151">${escapeHtml(item.label)} (${item.value})</text>`);
|
|
44
|
+
startAngle = endAngle;
|
|
45
|
+
});
|
|
46
|
+
return `<svg viewBox="0 0 ${W} ${H}" xmlns="http://www.w3.org/2000/svg" style="max-width:220px;height:auto">
|
|
47
|
+
${slices.join('\n ')}
|
|
48
|
+
${legends.join('\n ')}
|
|
49
|
+
</svg>`;
|
|
50
|
+
}
|
|
51
|
+
function buildStatsSection(decisions) {
|
|
52
|
+
const byCategory = new Map();
|
|
53
|
+
const byDecidedBy = { human: 0, ai: 0, both: 0 };
|
|
54
|
+
for (const d of decisions) {
|
|
55
|
+
byCategory.set(d.category, (byCategory.get(d.category) ?? 0) + 1);
|
|
56
|
+
if (d.decidedBy in byDecidedBy) {
|
|
57
|
+
byDecidedBy[d.decidedBy]++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const pieData = [...byCategory.entries()].map(([cat, val]) => ({
|
|
61
|
+
label: cat,
|
|
62
|
+
value: val,
|
|
63
|
+
color: CATEGORY_COLORS[cat],
|
|
64
|
+
}));
|
|
65
|
+
const pieSvg = buildPieSlicesSvg(pieData);
|
|
66
|
+
return `<section class="planu-card section-card">
|
|
67
|
+
<h2>Decision Statistics</h2>
|
|
68
|
+
<div class="stats-layout">
|
|
69
|
+
<div class="stats-pie">
|
|
70
|
+
<h3 class="stats-subtitle">By Category</h3>
|
|
71
|
+
${pieSvg}
|
|
72
|
+
</div>
|
|
73
|
+
<div class="stats-counts">
|
|
74
|
+
<h3 class="stats-subtitle">Total: ${decisions.length}</h3>
|
|
75
|
+
<div class="decidedby-grid">
|
|
76
|
+
<div class="stat-box">
|
|
77
|
+
<span class="stat-num">${byDecidedBy.human}</span>
|
|
78
|
+
<span class="stat-lbl">Human</span>
|
|
79
|
+
</div>
|
|
80
|
+
<div class="stat-box">
|
|
81
|
+
<span class="stat-num">${byDecidedBy.ai}</span>
|
|
82
|
+
<span class="stat-lbl">AI</span>
|
|
83
|
+
</div>
|
|
84
|
+
<div class="stat-box">
|
|
85
|
+
<span class="stat-num">${byDecidedBy.both}</span>
|
|
86
|
+
<span class="stat-lbl">Both</span>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
</div>
|
|
90
|
+
</div>
|
|
91
|
+
</section>`;
|
|
92
|
+
}
|
|
93
|
+
function buildSupersededSection(decisions) {
|
|
94
|
+
const superseded = decisions.filter((d) => d.status === 'superseded');
|
|
95
|
+
if (superseded.length === 0) {
|
|
96
|
+
return '';
|
|
97
|
+
}
|
|
98
|
+
const rows = superseded
|
|
99
|
+
.map((d) => `<tr>
|
|
100
|
+
<td>${escapeHtml(d.id)}</td>
|
|
101
|
+
<td class="text-strike">${escapeHtml(d.what)}</td>
|
|
102
|
+
<td>${escapeHtml(d.why)}</td>
|
|
103
|
+
<td>${escapeHtml(d.date)}</td>
|
|
104
|
+
</tr>`)
|
|
105
|
+
.join('\n');
|
|
106
|
+
return `<section class="planu-card section-card">
|
|
107
|
+
<h2>Superseded Decisions</h2>
|
|
108
|
+
<table class="simple-table">
|
|
109
|
+
<thead><tr><th>ID</th><th>Decision</th><th>Reason</th><th>Date</th></tr></thead>
|
|
110
|
+
<tbody>${rows}</tbody>
|
|
111
|
+
</table>
|
|
112
|
+
</section>`;
|
|
113
|
+
}
|
|
114
|
+
function buildPageCSS() {
|
|
115
|
+
return `
|
|
116
|
+
body { font-family: system-ui, sans-serif; margin: 0; padding: 0 16px 48px; background: var(--planu-bg); color: var(--planu-text); }
|
|
117
|
+
.page-title { font-size: 1.6rem; font-weight: 700; margin: 0 0 6px; }
|
|
118
|
+
.page-subtitle { color: #6b7280; margin: 0 0 24px; font-size: 0.95rem; }
|
|
119
|
+
.section-card { margin-bottom: 24px; }
|
|
120
|
+
h2 { font-size: 1.1rem; font-weight: 600; margin: 0 0 16px; color: var(--planu-primary); }
|
|
121
|
+
h3.stats-subtitle { font-size: 0.95rem; font-weight: 600; color: #374151; margin: 0 0 12px; }
|
|
122
|
+
.stats-layout { display: flex; gap: 32px; flex-wrap: wrap; align-items: flex-start; }
|
|
123
|
+
.stats-pie { flex: 1; min-width: 200px; }
|
|
124
|
+
.stats-counts { flex: 1; min-width: 150px; }
|
|
125
|
+
.decidedby-grid { display: flex; gap: 12px; }
|
|
126
|
+
.stat-box { text-align: center; padding: 10px 16px; background: #f3f4f6; border-radius: 8px; }
|
|
127
|
+
.stat-num { display: block; font-size: 1.6rem; font-weight: 700; color: var(--planu-primary); }
|
|
128
|
+
.stat-lbl { font-size: 0.78rem; color: #6b7280; }
|
|
129
|
+
.simple-table { width: 100%; border-collapse: collapse; font-size: 0.83rem; }
|
|
130
|
+
.simple-table th { background: #f3f4f6; padding: 8px 10px; text-align: left; border-bottom: 2px solid var(--planu-border); }
|
|
131
|
+
.simple-table td { padding: 8px 10px; border-bottom: 1px solid var(--planu-border); }
|
|
132
|
+
.text-strike { text-decoration: line-through; color: #9ca3af; }
|
|
133
|
+
.empty-state { text-align: center; padding: 64px 24px; color: #6b7280; }
|
|
134
|
+
.empty-state h3 { font-size: 1.2rem; }`;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Generate the decisions.html portal page.
|
|
138
|
+
*/
|
|
139
|
+
export function generateDecisionPage(decisions) {
|
|
140
|
+
const navbar = buildNavbar('decisions', '');
|
|
141
|
+
const themeCSS = getPortalThemeCSS();
|
|
142
|
+
const pageCSS = buildPageCSS();
|
|
143
|
+
const bodyContent = decisions.length === 0
|
|
144
|
+
? `<div class="empty-state"><h3>No decisions recorded yet.</h3><p>Use <code>log_decision</code> to capture architectural decisions.</p></div>`
|
|
145
|
+
: `${buildStatsSection(decisions)}
|
|
146
|
+
<section class="planu-card section-card">
|
|
147
|
+
<h2>Decision Timeline</h2>
|
|
148
|
+
${buildDecisionTimeline(decisions)}
|
|
149
|
+
</section>
|
|
150
|
+
${buildSupersededSection(decisions)}`;
|
|
151
|
+
return `<!DOCTYPE html>
|
|
152
|
+
<html lang="en">
|
|
153
|
+
<head>
|
|
154
|
+
<meta charset="UTF-8">
|
|
155
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
156
|
+
<title>Decisions — Planu Portal</title>
|
|
157
|
+
<style>${themeCSS}${pageCSS}${DECISION_TIMELINE_CSS}</style>
|
|
158
|
+
</head>
|
|
159
|
+
<body>
|
|
160
|
+
${navbar}
|
|
161
|
+
<main>
|
|
162
|
+
<h1 class="page-title">🏛️ Decision Log</h1>
|
|
163
|
+
<p class="page-subtitle">Architectural and strategic decisions timeline.</p>
|
|
164
|
+
${bodyContent}
|
|
165
|
+
</main>
|
|
166
|
+
</body>
|
|
167
|
+
</html>`;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=decision-page.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-page.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-page.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Decision } from '../../../types/decisions.js';
|
|
2
|
+
export declare const DECISION_TIMELINE_CSS = "\n .decision-timeline { position: relative; padding: 16px 0; }\n .timeline-line {\n position: absolute; left: 50%; top: 0; bottom: 0;\n width: 2px; background: #e5e7eb; transform: translateX(-50%);\n }\n .timeline-item {\n position: relative; width: 50%; padding: 0 40px 24px;\n box-sizing: border-box;\n }\n .timeline-item.side-left { left: 0; text-align: right; }\n .timeline-item.side-right { left: 50%; text-align: left; }\n .timeline-dot {\n position: absolute; top: 16px;\n width: 12px; height: 12px;\n border-radius: 50%; border: 2px solid white;\n box-shadow: 0 0 0 2px #e5e7eb;\n }\n .side-left .timeline-dot { right: -6px; }\n .side-right .timeline-dot { left: -6px; }\n .decision-card {\n background: white; border-radius: 8px;\n padding: 12px 14px; font-size: 0.86rem;\n box-shadow: 0 1px 3px rgba(0,0,0,0.08);\n }\n .card-header { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }\n .side-left .card-header { justify-content: flex-end; }\n .decision-id { font-weight: 700; color: #374151; }\n .decision-date { color: #9ca3af; font-size: 0.78rem; margin-left: auto; }\n .decision-what { font-weight: 600; color: #1f2937; margin-bottom: 4px; }\n .decision-why { color: #6b7280; font-size: 0.82rem; margin-bottom: 6px; }\n .decision-alternatives { margin-top: 6px; }\n .alt-label { font-size: 0.75rem; color: #9ca3af; margin-right: 4px; }\n .alt-chip {\n display: inline-block; padding: 2px 6px; border-radius: 4px;\n background: #f3f4f6; color: #374151; font-size: 0.75rem; margin: 2px;\n }\n .category-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }\n .spec-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; background: #dbeafe; color: #1d4ed8; }\n .badge-human { background: #d1fae5; color: #065f46; }\n .badge-ai { background: #ede9fe; color: #5b21b6; }\n .badge-both { background: #fef3c7; color: #92400e; }\n .status-superseded .decision-card { opacity: 0.65; }\n .text-strikethrough { text-decoration: line-through; color: #9ca3af; }\n .status-review .decision-card { background: #fffbeb; }\n @media (max-width: 640px) {\n .timeline-line { display: none; }\n .timeline-item, .timeline-item.side-right { left: 0; width: 100%; text-align: left; padding: 0 0 16px 16px; }\n .side-left .card-header { justify-content: flex-start; }\n .timeline-dot { left: 0 !important; right: auto !important; }\n }";
|
|
3
|
+
/**
|
|
4
|
+
* Build an alternating vertical timeline of decisions.
|
|
5
|
+
* Returns empty string when no decisions.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildDecisionTimeline(decisions: Decision[]): string;
|
|
8
|
+
//# sourceMappingURL=decision-timeline-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-timeline-builder.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-timeline-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAoC,MAAM,6BAA6B,CAAC;AAwE9F,eAAO,MAAM,qBAAqB,w+EAkD9B,CAAC;AAEL;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,QAAQ,EAAE,GAAG,MAAM,CAiBnE"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
function escapeHtml(s) {
|
|
2
|
+
return s
|
|
3
|
+
.replace(/&/g, '&')
|
|
4
|
+
.replace(/</g, '<')
|
|
5
|
+
.replace(/>/g, '>')
|
|
6
|
+
.replace(/"/g, '"');
|
|
7
|
+
}
|
|
8
|
+
const CATEGORY_COLORS = {
|
|
9
|
+
architecture: '#7c3aed', // purple
|
|
10
|
+
stack: '#2563eb', // blue
|
|
11
|
+
ux: '#16a34a', // green
|
|
12
|
+
business: '#ea580c', // orange
|
|
13
|
+
process: '#6b7280', // grey
|
|
14
|
+
};
|
|
15
|
+
const DECIDED_BY_BADGE = {
|
|
16
|
+
human: 'badge-human',
|
|
17
|
+
ai: 'badge-ai',
|
|
18
|
+
both: 'badge-both',
|
|
19
|
+
};
|
|
20
|
+
const STATUS_CLASS = {
|
|
21
|
+
active: 'status-active',
|
|
22
|
+
superseded: 'status-superseded',
|
|
23
|
+
under_review: 'status-review',
|
|
24
|
+
};
|
|
25
|
+
function formatDate(dateStr) {
|
|
26
|
+
try {
|
|
27
|
+
const d = new Date(dateStr);
|
|
28
|
+
return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return escapeHtml(dateStr);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function buildDecisionCard(decision, side) {
|
|
35
|
+
const color = CATEGORY_COLORS[decision.category];
|
|
36
|
+
const decidedBadgeClass = DECIDED_BY_BADGE[decision.decidedBy] ?? 'badge-human';
|
|
37
|
+
const statusClass = STATUS_CLASS[decision.status];
|
|
38
|
+
const alternativesHtml = decision.alternatives.length > 0
|
|
39
|
+
? `<div class="decision-alternatives">
|
|
40
|
+
<span class="alt-label">Alternatives considered:</span>
|
|
41
|
+
${decision.alternatives.map((a) => `<span class="alt-chip">${escapeHtml(a)}</span>`).join('')}
|
|
42
|
+
</div>`
|
|
43
|
+
: '';
|
|
44
|
+
const specBadge = decision.specId
|
|
45
|
+
? `<span class="spec-badge">${escapeHtml(decision.specId)}</span>`
|
|
46
|
+
: '';
|
|
47
|
+
return `<div class="timeline-item side-${side}" data-status="${decision.status}">
|
|
48
|
+
<div class="timeline-dot" style="background:${color}"></div>
|
|
49
|
+
<div class="decision-card ${statusClass}" style="border-left:4px solid ${color}">
|
|
50
|
+
<div class="card-header">
|
|
51
|
+
<span class="decision-id">${escapeHtml(decision.id)}</span>
|
|
52
|
+
<span class="planu-badge ${decidedBadgeClass}">${escapeHtml(decision.decidedBy)}</span>
|
|
53
|
+
<span class="category-badge" style="background:${color}20;color:${color}">${escapeHtml(decision.category)}</span>
|
|
54
|
+
${specBadge}
|
|
55
|
+
<span class="decision-date">${formatDate(decision.date)}</span>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="decision-what ${decision.status === 'superseded' ? 'text-strikethrough' : ''}">${escapeHtml(decision.what)}</div>
|
|
58
|
+
<div class="decision-why">${escapeHtml(decision.why)}</div>
|
|
59
|
+
${alternativesHtml}
|
|
60
|
+
</div>
|
|
61
|
+
</div>`;
|
|
62
|
+
}
|
|
63
|
+
export const DECISION_TIMELINE_CSS = `
|
|
64
|
+
.decision-timeline { position: relative; padding: 16px 0; }
|
|
65
|
+
.timeline-line {
|
|
66
|
+
position: absolute; left: 50%; top: 0; bottom: 0;
|
|
67
|
+
width: 2px; background: #e5e7eb; transform: translateX(-50%);
|
|
68
|
+
}
|
|
69
|
+
.timeline-item {
|
|
70
|
+
position: relative; width: 50%; padding: 0 40px 24px;
|
|
71
|
+
box-sizing: border-box;
|
|
72
|
+
}
|
|
73
|
+
.timeline-item.side-left { left: 0; text-align: right; }
|
|
74
|
+
.timeline-item.side-right { left: 50%; text-align: left; }
|
|
75
|
+
.timeline-dot {
|
|
76
|
+
position: absolute; top: 16px;
|
|
77
|
+
width: 12px; height: 12px;
|
|
78
|
+
border-radius: 50%; border: 2px solid white;
|
|
79
|
+
box-shadow: 0 0 0 2px #e5e7eb;
|
|
80
|
+
}
|
|
81
|
+
.side-left .timeline-dot { right: -6px; }
|
|
82
|
+
.side-right .timeline-dot { left: -6px; }
|
|
83
|
+
.decision-card {
|
|
84
|
+
background: white; border-radius: 8px;
|
|
85
|
+
padding: 12px 14px; font-size: 0.86rem;
|
|
86
|
+
box-shadow: 0 1px 3px rgba(0,0,0,0.08);
|
|
87
|
+
}
|
|
88
|
+
.card-header { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; margin-bottom: 6px; }
|
|
89
|
+
.side-left .card-header { justify-content: flex-end; }
|
|
90
|
+
.decision-id { font-weight: 700; color: #374151; }
|
|
91
|
+
.decision-date { color: #9ca3af; font-size: 0.78rem; margin-left: auto; }
|
|
92
|
+
.decision-what { font-weight: 600; color: #1f2937; margin-bottom: 4px; }
|
|
93
|
+
.decision-why { color: #6b7280; font-size: 0.82rem; margin-bottom: 6px; }
|
|
94
|
+
.decision-alternatives { margin-top: 6px; }
|
|
95
|
+
.alt-label { font-size: 0.75rem; color: #9ca3af; margin-right: 4px; }
|
|
96
|
+
.alt-chip {
|
|
97
|
+
display: inline-block; padding: 2px 6px; border-radius: 4px;
|
|
98
|
+
background: #f3f4f6; color: #374151; font-size: 0.75rem; margin: 2px;
|
|
99
|
+
}
|
|
100
|
+
.category-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; font-weight: 600; }
|
|
101
|
+
.spec-badge { display: inline-block; padding: 2px 6px; border-radius: 4px; font-size: 0.75rem; background: #dbeafe; color: #1d4ed8; }
|
|
102
|
+
.badge-human { background: #d1fae5; color: #065f46; }
|
|
103
|
+
.badge-ai { background: #ede9fe; color: #5b21b6; }
|
|
104
|
+
.badge-both { background: #fef3c7; color: #92400e; }
|
|
105
|
+
.status-superseded .decision-card { opacity: 0.65; }
|
|
106
|
+
.text-strikethrough { text-decoration: line-through; color: #9ca3af; }
|
|
107
|
+
.status-review .decision-card { background: #fffbeb; }
|
|
108
|
+
@media (max-width: 640px) {
|
|
109
|
+
.timeline-line { display: none; }
|
|
110
|
+
.timeline-item, .timeline-item.side-right { left: 0; width: 100%; text-align: left; padding: 0 0 16px 16px; }
|
|
111
|
+
.side-left .card-header { justify-content: flex-start; }
|
|
112
|
+
.timeline-dot { left: 0 !important; right: auto !important; }
|
|
113
|
+
}`;
|
|
114
|
+
/**
|
|
115
|
+
* Build an alternating vertical timeline of decisions.
|
|
116
|
+
* Returns empty string when no decisions.
|
|
117
|
+
*/
|
|
118
|
+
export function buildDecisionTimeline(decisions) {
|
|
119
|
+
if (decisions.length === 0) {
|
|
120
|
+
return '';
|
|
121
|
+
}
|
|
122
|
+
const sorted = [...decisions].sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
|
123
|
+
const items = sorted
|
|
124
|
+
.map((d, i) => buildDecisionCard(d, i % 2 === 0 ? 'left' : 'right'))
|
|
125
|
+
.join('\n ');
|
|
126
|
+
return `<div class="decision-timeline">
|
|
127
|
+
<div class="timeline-line"></div>
|
|
128
|
+
${items}
|
|
129
|
+
</div>`;
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=decision-timeline-builder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"decision-timeline-builder.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/decision-timeline-builder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export { getPortalThemeCSS } from './portal-theme.js';
|
|
2
|
+
export { buildNavbar, NAV_ITEMS } from './portal-navbar.js';
|
|
3
|
+
export type { PortalNavItem } from './portal-navbar.js';
|
|
4
|
+
export { buildBreadcrumbs, buildSpecNavigation } from './portal-breadcrumbs.js';
|
|
5
|
+
export type { BreadcrumbItem } from './portal-breadcrumbs.js';
|
|
6
|
+
export { buildQuickLinksSection } from './portal-landing-cards.js';
|
|
7
|
+
export { detectAvailablePages, detectAvailablePagesSync, DETECTABLE_PAGES, } from './portal-page-detector.js';
|
|
8
|
+
export { generateAnalyticsPage } from './analytics-page.js';
|
|
9
|
+
export { generateRoadmapPage } from './roadmap-page.js';
|
|
10
|
+
export { buildKanbanView } from './kanban-view-builder.js';
|
|
11
|
+
export { buildSvgBarChart } from './svg-charts/bar-chart.js';
|
|
12
|
+
export { buildSvgLineChart, buildSvgDualLineChart } from './svg-charts/line-chart.js';
|
|
13
|
+
export { buildSvgPieChart } from './svg-charts/pie-chart.js';
|
|
14
|
+
export { scaleLinear, svgText, CHART_COLORS } from './svg-charts/chart-utils.js';
|
|
15
|
+
export { computeVelocity } from './data-aggregators/velocity-aggregator.js';
|
|
16
|
+
export { computeBurndown } from './data-aggregators/burndown-aggregator.js';
|
|
17
|
+
export { detectBottlenecks } from './data-aggregators/bottleneck-detector.js';
|
|
18
|
+
export { computeAccuracy } from './data-aggregators/accuracy-aggregator.js';
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAChF,YAAY,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAG3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAGjF,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
// Planu — Portal infrastructure barrel export
|
|
2
|
+
export { getPortalThemeCSS } from './portal-theme.js';
|
|
3
|
+
export { buildNavbar, NAV_ITEMS } from './portal-navbar.js';
|
|
4
|
+
export { buildBreadcrumbs, buildSpecNavigation } from './portal-breadcrumbs.js';
|
|
5
|
+
export { buildQuickLinksSection } from './portal-landing-cards.js';
|
|
6
|
+
export { detectAvailablePages, detectAvailablePagesSync, DETECTABLE_PAGES, } from './portal-page-detector.js';
|
|
7
|
+
// SPEC-142 — Analytics, Roadmap, Kanban
|
|
8
|
+
export { generateAnalyticsPage } from './analytics-page.js';
|
|
9
|
+
export { generateRoadmapPage } from './roadmap-page.js';
|
|
10
|
+
export { buildKanbanView } from './kanban-view-builder.js';
|
|
11
|
+
// SVG chart builders
|
|
12
|
+
export { buildSvgBarChart } from './svg-charts/bar-chart.js';
|
|
13
|
+
export { buildSvgLineChart, buildSvgDualLineChart } from './svg-charts/line-chart.js';
|
|
14
|
+
export { buildSvgPieChart } from './svg-charts/pie-chart.js';
|
|
15
|
+
export { scaleLinear, svgText, CHART_COLORS } from './svg-charts/chart-utils.js';
|
|
16
|
+
// Data aggregators
|
|
17
|
+
export { computeVelocity } from './data-aggregators/velocity-aggregator.js';
|
|
18
|
+
export { computeBurndown } from './data-aggregators/burndown-aggregator.js';
|
|
19
|
+
export { detectBottlenecks } from './data-aggregators/bottleneck-detector.js';
|
|
20
|
+
export { computeAccuracy } from './data-aggregators/accuracy-aggregator.js';
|
|
21
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/index.ts"],"names":[],"mappings":"AAAA,8CAA8C;AAE9C,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EACL,oBAAoB,EACpB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,2BAA2B,CAAC;AAEnC,wCAAwC;AACxC,OAAO,EAAE,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,qBAAqB;AACrB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEjF,mBAAmB;AACnB,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC;AAC5E,OAAO,EAAE,iBAAiB,EAAE,MAAM,2CAA2C,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,MAAM,2CAA2C,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Spec } from '../../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build an HTML fragment (not a full page) containing a kanban board.
|
|
4
|
+
* Suitable for embedding inside the portal index.html.
|
|
5
|
+
* Returns empty string when specs is empty.
|
|
6
|
+
*/
|
|
7
|
+
export declare function buildKanbanView(specs: Spec[]): string;
|
|
8
|
+
//# sourceMappingURL=kanban-view-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kanban-view-builder.d.ts","sourceRoot":"","sources":["../../../../src/engine/doc-generator/portal/kanban-view-builder.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,yBAAyB,CAAC;AAyBpD;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,IAAI,EAAE,GAAG,MAAM,CAqCrD"}
|