@burdenoff/microfe-planmagnet 2026.930.23 → 2026.930.25
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/index.js +6 -5
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +154 -147
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +66 -62
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/comments/CommentThread.d.ts +8 -1
- package/dist/planmagnet/comments/CommentThread.d.ts.map +1 -1
- package/dist/planmagnet/comments/CommentThread.js +188 -181
- package/dist/planmagnet/comments/CommentThread.js.map +1 -1
- package/dist/planmagnet/comments/TargetComments.d.ts +10 -2
- package/dist/planmagnet/comments/TargetComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/TargetComments.js +10 -8
- package/dist/planmagnet/comments/TargetComments.js.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.d.ts +7 -1
- package/dist/planmagnet/comments/WorkItemComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.js +5 -4
- package/dist/planmagnet/comments/WorkItemComments.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +11 -0
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +305 -135
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/Pickers.d.ts +18 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
- package/dist/planmagnet/controls/Pickers.js +78 -1
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.d.ts +72 -0
- package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/PrioritizationControls.js +246 -0
- package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.d.ts +8 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.d.ts.map +1 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.js +217 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.js.map +1 -0
- package/dist/planmagnet/docs/DocDiffView.d.ts +26 -0
- package/dist/planmagnet/docs/DocDiffView.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocDiffView.js +52 -0
- package/dist/planmagnet/docs/DocDiffView.js.map +1 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.d.ts +13 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.js +308 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.js.map +1 -0
- package/dist/planmagnet/docs/DocVersionCompare.d.ts +7 -0
- package/dist/planmagnet/docs/DocVersionCompare.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocVersionCompare.js +105 -0
- package/dist/planmagnet/docs/DocVersionCompare.js.map +1 -0
- package/dist/planmagnet/hooks/usePresence.d.ts +39 -0
- package/dist/planmagnet/hooks/usePresence.d.ts.map +1 -0
- package/dist/planmagnet/hooks/usePresence.js +146 -0
- package/dist/planmagnet/hooks/usePresence.js.map +1 -0
- package/dist/planmagnet/index.d.ts +1 -0
- package/dist/planmagnet/index.d.ts.map +1 -1
- package/dist/planmagnet/index.js +1 -0
- package/dist/planmagnet/links/CodeLinksPanel.d.ts +22 -0
- package/dist/planmagnet/links/CodeLinksPanel.d.ts.map +1 -0
- package/dist/planmagnet/links/CodeLinksPanel.js +94 -0
- package/dist/planmagnet/links/CodeLinksPanel.js.map +1 -0
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +488 -217
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +131 -125
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +183 -99
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +113 -97
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js +363 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +96 -79
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +144 -128
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +205 -196
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +194 -160
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/presence/PresenceAvatars.d.ts +16 -0
- package/dist/planmagnet/presence/PresenceAvatars.d.ts.map +1 -0
- package/dist/planmagnet/presence/PresenceAvatars.js +56 -0
- package/dist/planmagnet/presence/PresenceAvatars.js.map +1 -0
- package/dist/planmagnet/product/ProductControls.d.ts +33 -0
- package/dist/planmagnet/product/ProductControls.d.ts.map +1 -1
- package/dist/planmagnet/product/ProductControls.js +593 -268
- package/dist/planmagnet/product/ProductControls.js.map +1 -1
- package/dist/planmagnet/product/routes.d.ts +10 -0
- package/dist/planmagnet/product/routes.d.ts.map +1 -0
- package/dist/planmagnet/product/routes.js +6 -0
- package/dist/planmagnet/product/routes.js.map +1 -0
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts +10 -0
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +235 -184
- package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
- package/dist/planmagnet/utils/graphqlErrors.d.ts +6 -0
- package/dist/planmagnet/utils/graphqlErrors.d.ts.map +1 -1
- package/dist/planmagnet/utils/graphqlErrors.js +5 -2
- package/dist/planmagnet/utils/graphqlErrors.js.map +1 -1
- package/dist/planmagnet/utils/lineDiff.d.ts +21 -0
- package/dist/planmagnet/utils/lineDiff.d.ts.map +1 -0
- package/dist/planmagnet/utils/lineDiff.js +36 -0
- package/dist/planmagnet/utils/lineDiff.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
2
|
import { PersonName as t } from "../people/People.js";
|
|
3
3
|
import { readUrlParam as n, writeUrlParam as r } from "../utils/urlParam.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
4
|
+
import { PresenceAvatars as i } from "../presence/PresenceAvatars.js";
|
|
5
|
+
import { usePresence as a } from "../hooks/usePresence.js";
|
|
6
|
+
import { PlanningFocusBadge as o, planningFocusLabel as s } from "../roadmaps/planningFocus.js";
|
|
7
|
+
import { QuadrantBadge as c } from "../controls/PrioritizationControls.js";
|
|
8
|
+
import { RemoveRoadmapItemButton as l, RoadmapHeaderActions as u, RoadmapItemDialog as d } from "../roadmaps/RoadmapControls.js";
|
|
9
|
+
import { RoadmapDiscussionPanel as f, RoadmapItemCommentsButton as p } from "../comments/RoadmapComments.js";
|
|
10
|
+
import { RoadmapNowNextLater as m } from "../roadmaps/RoadmapNowNextLater.js";
|
|
11
|
+
import { RoadmapPresentation as h } from "../roadmaps/RoadmapPresentation.js";
|
|
12
|
+
import { RoadmapReviewPanel as g } from "../roadmaps/RoadmapReviewPanel.js";
|
|
13
|
+
import { RoadmapVersionsPanel as _ } from "../roadmaps/RoadmapVersionsPanel.js";
|
|
14
|
+
import { useState as v } from "react";
|
|
15
|
+
import { Calendar as y, Loader2 as b, Map as x } from "lucide-react";
|
|
16
|
+
import { useParams as S } from "react-router";
|
|
17
|
+
import { gql as C } from "@apollo/client";
|
|
18
|
+
import { useQuery as w } from "@apollo/client/react";
|
|
19
|
+
import { jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
20
|
+
import { Badge as D, Card as O, CardContent as k, CardHeader as A, CardTitle as j } from "@burdenoff/fe-libs/ui";
|
|
18
21
|
//#region src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx
|
|
19
|
-
var
|
|
22
|
+
var M = C`
|
|
20
23
|
query GetRoadmapDetailPage($id: ID!) {
|
|
21
24
|
planmagnetRoadmap(id: $id) {
|
|
22
25
|
id
|
|
@@ -45,6 +48,11 @@ var k = b`
|
|
|
45
48
|
confidence
|
|
46
49
|
riskLevel
|
|
47
50
|
riskNote
|
|
51
|
+
moscow
|
|
52
|
+
kano
|
|
53
|
+
valueScore
|
|
54
|
+
effortScore
|
|
55
|
+
valueEffortQuadrant
|
|
48
56
|
featureRequestDemand {
|
|
49
57
|
requests
|
|
50
58
|
votes
|
|
@@ -52,7 +60,7 @@ var k = b`
|
|
|
52
60
|
}
|
|
53
61
|
}
|
|
54
62
|
}
|
|
55
|
-
`,
|
|
63
|
+
`, N = (e) => e === "timeline" || e === "nnl", P = {
|
|
56
64
|
TODO: "bg-gray-100 text-gray-800",
|
|
57
65
|
IN_PROGRESS: "bg-blue-100 text-blue-800",
|
|
58
66
|
DONE: "bg-green-100 text-green-800",
|
|
@@ -60,106 +68,110 @@ var k = b`
|
|
|
60
68
|
BLOCKED: "bg-red-100 text-red-800",
|
|
61
69
|
PLANNED: "bg-yellow-100 text-yellow-800"
|
|
62
70
|
};
|
|
63
|
-
function
|
|
64
|
-
let { workspaceId:
|
|
71
|
+
function F() {
|
|
72
|
+
let { workspaceId: C } = e(), F = S().id ?? "", I = a("ROADMAP", F || null), [L, R] = v(() => {
|
|
65
73
|
let e = n("view");
|
|
66
|
-
return
|
|
67
|
-
}),
|
|
68
|
-
|
|
69
|
-
}, { data:
|
|
70
|
-
variables: { id:
|
|
71
|
-
skip: !
|
|
74
|
+
return N(e) ? e : "timeline";
|
|
75
|
+
}), z = (e) => {
|
|
76
|
+
R(e), r("view", e === "timeline" ? null : e);
|
|
77
|
+
}, { data: B, loading: V, error: H } = w(M, {
|
|
78
|
+
variables: { id: F },
|
|
79
|
+
skip: !F
|
|
72
80
|
});
|
|
73
|
-
if (
|
|
81
|
+
if (V && !B) return /* @__PURE__ */ T("div", {
|
|
74
82
|
className: "flex items-center justify-center p-12",
|
|
75
|
-
children: /* @__PURE__ */
|
|
83
|
+
children: /* @__PURE__ */ T(b, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
76
84
|
});
|
|
77
|
-
if (
|
|
85
|
+
if (H) return /* @__PURE__ */ E("div", {
|
|
78
86
|
className: "p-6 text-destructive",
|
|
79
|
-
children: ["Failed to load roadmap: ",
|
|
87
|
+
children: ["Failed to load roadmap: ", H.message]
|
|
80
88
|
});
|
|
81
|
-
let
|
|
82
|
-
if (!
|
|
89
|
+
let U = B?.planmagnetRoadmap;
|
|
90
|
+
if (!U) return /* @__PURE__ */ T("div", {
|
|
83
91
|
className: "p-6 text-muted-foreground",
|
|
84
92
|
children: "Roadmap not found."
|
|
85
93
|
});
|
|
86
|
-
let
|
|
87
|
-
for (let e of
|
|
94
|
+
let W = (U.items ?? []).slice().sort((e, t) => e.sortOrder - t.sortOrder), G = W.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), K = W.length > 0 ? Math.round(G.length / W.length * 100) : 0, q = [...new Set(W.map((e) => e.swimlane).filter(Boolean))], J = {};
|
|
95
|
+
for (let e of W) {
|
|
88
96
|
let t = e.swimlane ?? "Uncategorized";
|
|
89
|
-
|
|
97
|
+
J[t] || (J[t] = []), J[t].push(e);
|
|
90
98
|
}
|
|
91
|
-
return /* @__PURE__ */
|
|
99
|
+
return /* @__PURE__ */ E("div", {
|
|
92
100
|
className: "space-y-6 p-6",
|
|
93
101
|
children: [
|
|
94
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ E("div", {
|
|
95
103
|
className: "flex items-center justify-between",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
className: "
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
104
|
+
children: [/* @__PURE__ */ E("div", { children: [
|
|
105
|
+
/* @__PURE__ */ E("div", {
|
|
106
|
+
className: "flex items-center gap-2",
|
|
107
|
+
children: [/* @__PURE__ */ T(x, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ T("h1", {
|
|
108
|
+
className: "text-2xl font-semibold",
|
|
109
|
+
children: U.name
|
|
110
|
+
})]
|
|
111
|
+
}),
|
|
112
|
+
/* @__PURE__ */ T("p", {
|
|
113
|
+
className: "text-sm text-muted-foreground mt-1",
|
|
114
|
+
children: U.description ?? "No description"
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ T(i, { others: I.others })
|
|
117
|
+
] }), /* @__PURE__ */ E("div", {
|
|
106
118
|
className: "flex flex-col items-end gap-2",
|
|
107
|
-
children: [/* @__PURE__ */
|
|
119
|
+
children: [/* @__PURE__ */ E("div", {
|
|
108
120
|
className: "flex items-center gap-2",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
110
|
-
roadmapName:
|
|
111
|
-
roadmapDescription:
|
|
112
|
-
view:
|
|
113
|
-
items:
|
|
114
|
-
}), /* @__PURE__ */
|
|
115
|
-
roadmapId:
|
|
116
|
-
name:
|
|
121
|
+
children: [/* @__PURE__ */ T(h, {
|
|
122
|
+
roadmapName: U.name,
|
|
123
|
+
roadmapDescription: U.description,
|
|
124
|
+
view: L,
|
|
125
|
+
items: W
|
|
126
|
+
}), /* @__PURE__ */ T(u, {
|
|
127
|
+
roadmapId: U.id,
|
|
128
|
+
name: U.name
|
|
117
129
|
})]
|
|
118
|
-
}), /* @__PURE__ */
|
|
130
|
+
}), /* @__PURE__ */ E("div", {
|
|
119
131
|
className: "flex gap-2",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
121
|
-
variant:
|
|
132
|
+
children: [/* @__PURE__ */ T(D, {
|
|
133
|
+
variant: U.isPublic ? "default" : "secondary",
|
|
122
134
|
className: "capitalize",
|
|
123
|
-
children:
|
|
124
|
-
}), /* @__PURE__ */
|
|
135
|
+
children: U.isPublic ? "public" : "private"
|
|
136
|
+
}), /* @__PURE__ */ T(o, { focus: U.planningFocus })]
|
|
125
137
|
})]
|
|
126
138
|
})]
|
|
127
139
|
}),
|
|
128
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ T("div", {
|
|
129
141
|
className: "grid grid-cols-2 md:grid-cols-5 gap-4",
|
|
130
142
|
children: [
|
|
131
143
|
{
|
|
132
144
|
label: "Progress",
|
|
133
|
-
value: `${
|
|
145
|
+
value: `${K}%`
|
|
134
146
|
},
|
|
135
147
|
{
|
|
136
148
|
label: "Total Items",
|
|
137
|
-
value:
|
|
149
|
+
value: W.length
|
|
138
150
|
},
|
|
139
151
|
{
|
|
140
152
|
label: "Completed",
|
|
141
|
-
value:
|
|
153
|
+
value: G.length
|
|
142
154
|
},
|
|
143
155
|
{
|
|
144
156
|
label: "Planning focus",
|
|
145
|
-
value:
|
|
157
|
+
value: s(U.planningFocus)
|
|
146
158
|
},
|
|
147
159
|
{
|
|
148
160
|
label: "Visibility",
|
|
149
|
-
value:
|
|
161
|
+
value: U.isPublic ? "Public" : "Private"
|
|
150
162
|
}
|
|
151
|
-
].map((e) => /* @__PURE__ */
|
|
163
|
+
].map((e) => /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
|
|
152
164
|
className: "pt-4 text-center",
|
|
153
|
-
children: [/* @__PURE__ */
|
|
165
|
+
children: [/* @__PURE__ */ T("p", {
|
|
154
166
|
className: "text-xs text-muted-foreground",
|
|
155
167
|
children: e.label
|
|
156
|
-
}), /* @__PURE__ */
|
|
168
|
+
}), /* @__PURE__ */ T("p", {
|
|
157
169
|
className: "text-lg font-semibold capitalize",
|
|
158
170
|
children: e.value
|
|
159
171
|
})]
|
|
160
172
|
}) }, e.label))
|
|
161
173
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
174
|
+
/* @__PURE__ */ T("div", {
|
|
163
175
|
className: "flex items-center gap-2",
|
|
164
176
|
role: "group",
|
|
165
177
|
"aria-label": "Roadmap view",
|
|
@@ -170,68 +182,68 @@ function M() {
|
|
|
170
182
|
}, {
|
|
171
183
|
key: "nnl",
|
|
172
184
|
label: "Now / Next / Later"
|
|
173
|
-
}].map((e) => /* @__PURE__ */
|
|
185
|
+
}].map((e) => /* @__PURE__ */ T("button", {
|
|
174
186
|
type: "button",
|
|
175
|
-
"aria-pressed":
|
|
176
|
-
onClick: () =>
|
|
177
|
-
className: `px-2 py-1 text-xs rounded-md ${
|
|
187
|
+
"aria-pressed": L === e.key,
|
|
188
|
+
onClick: () => z(e.key),
|
|
189
|
+
className: `px-2 py-1 text-xs rounded-md ${L === e.key ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
178
190
|
children: e.label
|
|
179
191
|
}, e.key))
|
|
180
192
|
}),
|
|
181
|
-
|
|
182
|
-
|
|
193
|
+
L === "nnl" && /* @__PURE__ */ T(m, { roadmapId: U.id }),
|
|
194
|
+
L === "timeline" && /* @__PURE__ */ E("div", {
|
|
183
195
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
196
|
+
children: [/* @__PURE__ */ E("div", {
|
|
185
197
|
className: "lg:col-span-2 space-y-6",
|
|
186
198
|
children: [
|
|
187
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ E(O, { children: [/* @__PURE__ */ E(A, {
|
|
188
200
|
className: "flex flex-row items-center justify-between gap-2",
|
|
189
|
-
children: [/* @__PURE__ */
|
|
190
|
-
roadmapId:
|
|
191
|
-
lanes:
|
|
201
|
+
children: [/* @__PURE__ */ T(j, { children: "Progress Overview" }), /* @__PURE__ */ T(d, {
|
|
202
|
+
roadmapId: U.id,
|
|
203
|
+
lanes: q
|
|
192
204
|
})]
|
|
193
|
-
}), /* @__PURE__ */
|
|
205
|
+
}), /* @__PURE__ */ E(k, { children: [/* @__PURE__ */ E("div", {
|
|
194
206
|
className: "flex justify-between text-sm mb-2",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
207
|
+
children: [/* @__PURE__ */ T("span", {
|
|
196
208
|
className: "text-muted-foreground",
|
|
197
209
|
children: "Overall Completion"
|
|
198
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ E("span", {
|
|
199
211
|
className: "font-medium",
|
|
200
|
-
children: [
|
|
212
|
+
children: [K, "%"]
|
|
201
213
|
})]
|
|
202
|
-
}), /* @__PURE__ */
|
|
214
|
+
}), /* @__PURE__ */ T("div", {
|
|
203
215
|
className: "w-full h-4 rounded-full bg-muted overflow-hidden",
|
|
204
|
-
children: /* @__PURE__ */
|
|
216
|
+
children: /* @__PURE__ */ T("div", {
|
|
205
217
|
className: "h-full bg-primary rounded-full transition-all",
|
|
206
|
-
style: { width: `${
|
|
218
|
+
style: { width: `${K}%` }
|
|
207
219
|
})
|
|
208
220
|
})] })] }),
|
|
209
|
-
|
|
221
|
+
W.length === 0 && /* @__PURE__ */ E("p", {
|
|
210
222
|
className: "rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground",
|
|
211
223
|
children: [
|
|
212
224
|
"This roadmap has no items yet. Use ",
|
|
213
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ T("span", {
|
|
214
226
|
className: "font-medium",
|
|
215
227
|
children: "Add item"
|
|
216
228
|
}),
|
|
217
229
|
" to plan the first one."
|
|
218
230
|
]
|
|
219
231
|
}),
|
|
220
|
-
Object.entries(
|
|
232
|
+
Object.entries(J).map(([e, t]) => /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ E(j, { children: [
|
|
221
233
|
e,
|
|
222
234
|
" (",
|
|
223
235
|
t.length,
|
|
224
236
|
")"
|
|
225
|
-
] }) }), /* @__PURE__ */
|
|
237
|
+
] }) }), /* @__PURE__ */ T(k, {
|
|
226
238
|
className: "space-y-3",
|
|
227
|
-
children: t.map((e) => /* @__PURE__ */
|
|
239
|
+
children: t.map((e) => /* @__PURE__ */ E("div", {
|
|
228
240
|
className: "flex items-center justify-between border rounded-md p-3",
|
|
229
|
-
children: [/* @__PURE__ */
|
|
230
|
-
/* @__PURE__ */
|
|
241
|
+
children: [/* @__PURE__ */ E("div", { children: [
|
|
242
|
+
/* @__PURE__ */ T("p", {
|
|
231
243
|
className: "text-sm font-medium",
|
|
232
244
|
children: e.title
|
|
233
245
|
}),
|
|
234
|
-
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */
|
|
246
|
+
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */ E("p", {
|
|
235
247
|
className: "text-xs text-muted-foreground",
|
|
236
248
|
"data-testid": "roadmap-item-demand",
|
|
237
249
|
children: [
|
|
@@ -245,41 +257,45 @@ function M() {
|
|
|
245
257
|
e.featureRequestDemand.votes === 1 ? "" : "s"
|
|
246
258
|
]
|
|
247
259
|
}),
|
|
248
|
-
e.description && /* @__PURE__ */
|
|
260
|
+
e.description && /* @__PURE__ */ T("p", {
|
|
249
261
|
className: "text-xs text-muted-foreground",
|
|
250
262
|
children: e.description.length > 60 ? `${e.description.slice(0, 60)}…` : e.description
|
|
251
263
|
}),
|
|
252
|
-
(e.startDate || e.endDate) && /* @__PURE__ */
|
|
264
|
+
(e.startDate || e.endDate) && /* @__PURE__ */ E("div", {
|
|
253
265
|
className: "flex items-center gap-1 mt-1 text-xs text-muted-foreground",
|
|
254
266
|
children: [
|
|
255
|
-
/* @__PURE__ */
|
|
267
|
+
/* @__PURE__ */ T(y, { className: "h-3 w-3" }),
|
|
256
268
|
e.startDate ? new Date(e.startDate).toLocaleDateString() : "?",
|
|
257
269
|
" - ",
|
|
258
270
|
e.endDate ? new Date(e.endDate).toLocaleDateString() : "?"
|
|
259
271
|
]
|
|
260
272
|
})
|
|
261
|
-
] }), /* @__PURE__ */
|
|
273
|
+
] }), /* @__PURE__ */ E("div", {
|
|
262
274
|
className: "flex items-center gap-2",
|
|
263
275
|
children: [
|
|
264
|
-
e.color && /* @__PURE__ */
|
|
276
|
+
e.color && /* @__PURE__ */ T("div", {
|
|
265
277
|
className: "h-3 w-3 rounded-full",
|
|
266
278
|
style: { backgroundColor: e.color }
|
|
267
279
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
-
className: `text-xs px-2 py-0.5 rounded-full capitalize ${
|
|
280
|
+
/* @__PURE__ */ T("span", {
|
|
281
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${P[e.status] ?? "bg-gray-100"}`,
|
|
270
282
|
children: e.status.toLowerCase().replace("_", " ")
|
|
271
283
|
}),
|
|
272
|
-
/* @__PURE__ */
|
|
273
|
-
|
|
284
|
+
/* @__PURE__ */ T(c, {
|
|
285
|
+
quadrant: e.valueEffortQuadrant,
|
|
286
|
+
hideWhenUnscored: !0
|
|
287
|
+
}),
|
|
288
|
+
/* @__PURE__ */ T(p, {
|
|
289
|
+
roadmapId: U.id,
|
|
274
290
|
itemId: e.id,
|
|
275
291
|
itemTitle: e.title
|
|
276
292
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
278
|
-
roadmapId:
|
|
293
|
+
/* @__PURE__ */ T(d, {
|
|
294
|
+
roadmapId: U.id,
|
|
279
295
|
item: e,
|
|
280
|
-
lanes:
|
|
296
|
+
lanes: q
|
|
281
297
|
}),
|
|
282
|
-
/* @__PURE__ */
|
|
298
|
+
/* @__PURE__ */ T(l, {
|
|
283
299
|
id: e.id,
|
|
284
300
|
title: e.title
|
|
285
301
|
})
|
|
@@ -288,57 +304,57 @@ function M() {
|
|
|
288
304
|
}, e.id))
|
|
289
305
|
})] }, e))
|
|
290
306
|
]
|
|
291
|
-
}), /* @__PURE__ */
|
|
307
|
+
}), /* @__PURE__ */ T("div", {
|
|
292
308
|
className: "space-y-6",
|
|
293
|
-
children: /* @__PURE__ */
|
|
309
|
+
children: /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Details" }) }), /* @__PURE__ */ E(k, {
|
|
294
310
|
className: "space-y-2 text-sm",
|
|
295
311
|
children: [
|
|
296
|
-
/* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ E("div", {
|
|
297
313
|
className: "flex justify-between",
|
|
298
|
-
children: [/* @__PURE__ */
|
|
314
|
+
children: [/* @__PURE__ */ T("span", {
|
|
299
315
|
className: "text-muted-foreground",
|
|
300
316
|
children: "Planning focus"
|
|
301
|
-
}), /* @__PURE__ */
|
|
317
|
+
}), /* @__PURE__ */ T(o, { focus: U.planningFocus })]
|
|
302
318
|
}),
|
|
303
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ E("div", {
|
|
304
320
|
className: "flex justify-between",
|
|
305
|
-
children: [/* @__PURE__ */
|
|
321
|
+
children: [/* @__PURE__ */ T("span", {
|
|
306
322
|
className: "text-muted-foreground",
|
|
307
323
|
children: "Owner"
|
|
308
|
-
}), /* @__PURE__ */
|
|
324
|
+
}), /* @__PURE__ */ T("span", { children: /* @__PURE__ */ T(t, { id: U.ownerId }) })]
|
|
309
325
|
}),
|
|
310
|
-
/* @__PURE__ */
|
|
326
|
+
/* @__PURE__ */ E("div", {
|
|
311
327
|
className: "flex justify-between",
|
|
312
|
-
children: [/* @__PURE__ */
|
|
328
|
+
children: [/* @__PURE__ */ T("span", {
|
|
313
329
|
className: "text-muted-foreground",
|
|
314
330
|
children: "Created"
|
|
315
|
-
}), /* @__PURE__ */
|
|
331
|
+
}), /* @__PURE__ */ T("span", { children: new Date(U.createdAt).toLocaleDateString() })]
|
|
316
332
|
}),
|
|
317
|
-
/* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ E("div", {
|
|
318
334
|
className: "flex justify-between",
|
|
319
|
-
children: [/* @__PURE__ */
|
|
335
|
+
children: [/* @__PURE__ */ T("span", {
|
|
320
336
|
className: "text-muted-foreground",
|
|
321
337
|
children: "Updated"
|
|
322
|
-
}), /* @__PURE__ */
|
|
338
|
+
}), /* @__PURE__ */ T("span", { children: new Date(U.updatedAt).toLocaleDateString() })]
|
|
323
339
|
})
|
|
324
340
|
]
|
|
325
341
|
})] })
|
|
326
342
|
})]
|
|
327
343
|
}),
|
|
328
|
-
/* @__PURE__ */
|
|
344
|
+
/* @__PURE__ */ E("div", {
|
|
329
345
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
330
|
-
children: [/* @__PURE__ */
|
|
346
|
+
children: [/* @__PURE__ */ T("div", {
|
|
331
347
|
className: "lg:col-span-2",
|
|
332
|
-
children: /* @__PURE__ */
|
|
333
|
-
}), /* @__PURE__ */
|
|
348
|
+
children: /* @__PURE__ */ T(f, { roadmapId: U.id })
|
|
349
|
+
}), /* @__PURE__ */ E("div", {
|
|
334
350
|
className: "space-y-6",
|
|
335
|
-
children: [/* @__PURE__ */
|
|
351
|
+
children: [/* @__PURE__ */ T(g, { roadmapId: U.id }), /* @__PURE__ */ T(_, { roadmapId: U.id })]
|
|
336
352
|
})]
|
|
337
353
|
})
|
|
338
354
|
]
|
|
339
355
|
});
|
|
340
356
|
}
|
|
341
357
|
//#endregion
|
|
342
|
-
export {
|
|
358
|
+
export { F as ProductsRoadmapDetailPagePage };
|
|
343
359
|
|
|
344
360
|
//# sourceMappingURL=ProductsRoadmapDetailPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsRoadmapDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Map, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport { PlanningFocusBadge, planningFocusLabel } from '../roadmaps/planningFocus';\nimport {\n RemoveRoadmapItemButton,\n RoadmapHeaderActions,\n RoadmapItemDialog,\n} from '../roadmaps/RoadmapControls';\nimport { RoadmapNowNextLater } from '../roadmaps/RoadmapNowNextLater';\nimport { RoadmapPresentation } from '../roadmaps/RoadmapPresentation';\nimport { RoadmapReviewPanel } from '../roadmaps/RoadmapReviewPanel';\nimport { RoadmapVersionsPanel } from '../roadmaps/RoadmapVersionsPanel';\nimport { RoadmapDiscussionPanel, RoadmapItemCommentsButton } from '../comments/RoadmapComments';\nimport { readUrlParam, writeUrlParam } from '../utils/urlParam';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetailPage($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n projectId\n milestoneId\n horizon\n confidence\n riskLevel\n riskNote\n featureRequestDemand {\n requests\n votes\n }\n }\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n color: string | null;\n swimlane: string | null;\n sortOrder: number;\n projectId: string | null;\n milestoneId: string | null;\n /** Now/Next/Later placement (PRM-06) — read here only to hand off to `RoadmapItemDialog`'s edit form; the\n * Now/Next/Later view itself reads and writes these through its own query/mutation, not this one. */\n horizon: string | null;\n confidence: number | null;\n riskLevel: string | null;\n riskNote: string | null;\n featureRequestDemand: { requests: number; votes: number } | null;\n}\n\ntype RoadmapView = 'timeline' | 'nnl';\nconst isRoadmapView = (v: string | null): v is RoadmapView => v === 'timeline' || v === 'nnl';\n\nconst statusColor: Record<string, string> = {\n TODO: 'bg-gray-100 text-gray-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n DONE: 'bg-green-100 text-green-800',\n COMPLETED: 'bg-green-100 text-green-800',\n BLOCKED: 'bg-red-100 text-red-800',\n PLANNED: 'bg-yellow-100 text-yellow-800',\n};\n\nexport function ProductsRoadmapDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n // Timeline | Now-Next-Later (PRM-06): persisted in the URL (`?view=nnl`) so the applied view is linkable and\n // survives a reload, without touching the host shell's own router — same technique as\n // `board/boardViewConfig.ts`'s `?view=` for saved board views (see `utils/urlParam.ts`).\n const [view, setViewState] = useState<RoadmapView>(() => {\n const fromUrl = readUrlParam('view');\n return isRoadmapView(fromUrl) ? fromUrl : 'timeline';\n });\n const setView = (next: RoadmapView) => {\n setViewState(next);\n writeUrlParam('view', next === 'timeline' ? null : next);\n };\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load roadmap: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Roadmap not found.</div>;\n\n const items: RoadmapItemNode[] = (roadmap.items ?? [])\n .slice()\n .sort((a: RoadmapItemNode, b: RoadmapItemNode) => a.sortOrder - b.sortOrder);\n const completedItems = items.filter((i) => i.status === 'COMPLETED' || i.status === 'DONE');\n const progress = items.length > 0 ? Math.round((completedItems.length / items.length) * 100) : 0;\n\n const lanes = [...new Set(items.map((i) => i.swimlane).filter(Boolean))] as string[];\n\n // Group items by swimlane\n const swimlaneGroups: Record<string, RoadmapItemNode[]> = {};\n for (const item of items) {\n const lane = item.swimlane ?? 'Uncategorized';\n if (!swimlaneGroups[lane]) swimlaneGroups[lane] = [];\n swimlaneGroups[lane].push(item);\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Map className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex items-center gap-2\">\n {/* PRM-11: reuses this same `items` list (already carries horizon/confidence/risk/dates) rather\n than firing a query of its own. */}\n <RoadmapPresentation\n roadmapName={roadmap.name}\n roadmapDescription={roadmap.description}\n view={view}\n items={items}\n />\n <RoadmapHeaderActions roadmapId={roadmap.id} name={roadmap.name} />\n </div>\n <div className=\"flex gap-2\">\n <Badge variant={roadmap.isPublic ? 'default' : 'secondary'} className=\"capitalize\">\n {roadmap.isPublic ? 'public' : 'private'}\n </Badge>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-4\">\n {[\n { label: 'Progress', value: `${progress}%` },\n { label: 'Total Items', value: items.length },\n { label: 'Completed', value: completedItems.length },\n { label: 'Planning focus', value: planningFocusLabel(roadmap.planningFocus) },\n { label: 'Visibility', value: roadmap.isPublic ? 'Public' : 'Private' },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-xs text-muted-foreground\">{s.label}</p>\n <p className=\"text-lg font-semibold capitalize\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div\n className=\"flex items-center gap-2\"\n role=\"group\"\n aria-label=\"Roadmap view\"\n data-testid=\"roadmap-view-toggle\"\n >\n {(\n [\n { key: 'timeline', label: 'Timeline' },\n { key: 'nnl', label: 'Now / Next / Later' },\n ] as const\n ).map((v) => (\n <button\n key={v.key}\n type=\"button\"\n aria-pressed={view === v.key}\n onClick={() => setView(v.key)}\n className={`px-2 py-1 text-xs rounded-md ${view === v.key ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {v.label}\n </button>\n ))}\n </div>\n\n {view === 'nnl' && <RoadmapNowNextLater roadmapId={roadmap.id} />}\n\n {view === 'timeline' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2\">\n <CardTitle>Progress Overview</CardTitle>\n <RoadmapItemDialog roadmapId={roadmap.id} lanes={lanes} />\n </CardHeader>\n <CardContent>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Overall Completion</span>\n <span className=\"font-medium\">{progress}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </CardContent>\n </Card>\n\n {items.length === 0 && (\n <p className=\"rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground\">\n This roadmap has no items yet. Use <span className=\"font-medium\">Add item</span> to\n plan the first one.\n </p>\n )}\n {Object.entries(swimlaneGroups).map(([lane, laneItems]) => (\n <Card key={lane}>\n <CardHeader>\n <CardTitle>\n {lane} ({laneItems.length})\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {laneItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n {/* PRM-24: demand behind the initiative, counted by the server. */}\n {item.featureRequestDemand && item.featureRequestDemand.requests > 0 && (\n <p\n className=\"text-xs text-muted-foreground\"\n data-testid=\"roadmap-item-demand\"\n >\n {item.featureRequestDemand.requests} feature request\n {item.featureRequestDemand.requests === 1 ? '' : 's'} ·{' '}\n {item.featureRequestDemand.votes} vote\n {item.featureRequestDemand.votes === 1 ? '' : 's'}\n </p>\n )}\n {item.description && (\n <p className=\"text-xs text-muted-foreground\">\n {item.description.length > 60\n ? `${item.description.slice(0, 60)}…`\n : item.description}\n </p>\n )}\n {(item.startDate || item.endDate) && (\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {item.startDate ? new Date(item.startDate).toLocaleDateString() : '?'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : '?'}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {item.color && (\n <div\n className=\"h-3 w-3 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[item.status] ?? 'bg-gray-100'}`}\n >\n {item.status.toLowerCase().replace('_', ' ')}\n </span>\n <RoadmapItemCommentsButton\n roadmapId={roadmap.id}\n itemId={item.id}\n itemTitle={item.title}\n />\n <RoadmapItemDialog roadmapId={roadmap.id} item={item} lanes={lanes} />\n <RemoveRoadmapItemButton id={item.id} title={item.title} />\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Planning focus</span>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {/* PRM-11: collaboration (discussion, review, versions) is roadmap-level state, not tied to either view\n above — shown regardless of the Timeline/Now-Next-Later toggle. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n <RoadmapDiscussionPanel roadmapId={roadmap.id} />\n </div>\n <div className=\"space-y-6\">\n <RoadmapReviewPanel roadmapId={roadmap.id} />\n <RoadmapVersionsPanel roadmapId={roadmap.id} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Map, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport { PlanningFocusBadge, planningFocusLabel } from '../roadmaps/planningFocus';\nimport {\n RemoveRoadmapItemButton,\n RoadmapHeaderActions,\n RoadmapItemDialog,\n} from '../roadmaps/RoadmapControls';\nimport { RoadmapNowNextLater } from '../roadmaps/RoadmapNowNextLater';\nimport { RoadmapPresentation } from '../roadmaps/RoadmapPresentation';\nimport { RoadmapReviewPanel } from '../roadmaps/RoadmapReviewPanel';\nimport { RoadmapVersionsPanel } from '../roadmaps/RoadmapVersionsPanel';\nimport { RoadmapDiscussionPanel, RoadmapItemCommentsButton } from '../comments/RoadmapComments';\nimport { readUrlParam, writeUrlParam } from '../utils/urlParam';\nimport { QuadrantBadge } from '../controls/PrioritizationControls';\nimport { usePresence } from '../hooks/usePresence';\nimport { PresenceAvatars } from '../presence/PresenceAvatars';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetailPage($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n projectId\n milestoneId\n horizon\n confidence\n riskLevel\n riskNote\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n featureRequestDemand {\n requests\n votes\n }\n }\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n color: string | null;\n swimlane: string | null;\n sortOrder: number;\n projectId: string | null;\n milestoneId: string | null;\n /** Now/Next/Later placement (PRM-06) — read here only to hand off to `RoadmapItemDialog`'s edit form; the\n * Now/Next/Later view itself reads and writes these through its own query/mutation, not this one. */\n horizon: string | null;\n confidence: number | null;\n riskLevel: string | null;\n riskNote: string | null;\n /** PRM-27 — read here only to show on the row and hand off to RoadmapItemDialog's edit form. */\n moscow: string | null;\n kano: string | null;\n valueScore: number | null;\n effortScore: number | null;\n valueEffortQuadrant: string | null;\n featureRequestDemand: { requests: number; votes: number } | null;\n}\n\ntype RoadmapView = 'timeline' | 'nnl';\nconst isRoadmapView = (v: string | null): v is RoadmapView => v === 'timeline' || v === 'nnl';\n\nconst statusColor: Record<string, string> = {\n TODO: 'bg-gray-100 text-gray-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n DONE: 'bg-green-100 text-green-800',\n COMPLETED: 'bg-green-100 text-green-800',\n BLOCKED: 'bg-red-100 text-red-800',\n PLANNED: 'bg-yellow-100 text-yellow-800',\n};\n\nexport function ProductsRoadmapDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n // CWF-24: who else is looking at this roadmap right now.\n const presence = usePresence('ROADMAP', roadmapId || null);\n // Timeline | Now-Next-Later (PRM-06): persisted in the URL (`?view=nnl`) so the applied view is linkable and\n // survives a reload, without touching the host shell's own router — same technique as\n // `board/boardViewConfig.ts`'s `?view=` for saved board views (see `utils/urlParam.ts`).\n const [view, setViewState] = useState<RoadmapView>(() => {\n const fromUrl = readUrlParam('view');\n return isRoadmapView(fromUrl) ? fromUrl : 'timeline';\n });\n const setView = (next: RoadmapView) => {\n setViewState(next);\n writeUrlParam('view', next === 'timeline' ? null : next);\n };\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load roadmap: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Roadmap not found.</div>;\n\n const items: RoadmapItemNode[] = (roadmap.items ?? [])\n .slice()\n .sort((a: RoadmapItemNode, b: RoadmapItemNode) => a.sortOrder - b.sortOrder);\n const completedItems = items.filter((i) => i.status === 'COMPLETED' || i.status === 'DONE');\n const progress = items.length > 0 ? Math.round((completedItems.length / items.length) * 100) : 0;\n\n const lanes = [...new Set(items.map((i) => i.swimlane).filter(Boolean))] as string[];\n\n // Group items by swimlane\n const swimlaneGroups: Record<string, RoadmapItemNode[]> = {};\n for (const item of items) {\n const lane = item.swimlane ?? 'Uncategorized';\n if (!swimlaneGroups[lane]) swimlaneGroups[lane] = [];\n swimlaneGroups[lane].push(item);\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Map className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {roadmap.description ?? 'No description'}\n </p>\n <PresenceAvatars others={presence.others} />\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex items-center gap-2\">\n {/* PRM-11: reuses this same `items` list (already carries horizon/confidence/risk/dates) rather\n than firing a query of its own. */}\n <RoadmapPresentation\n roadmapName={roadmap.name}\n roadmapDescription={roadmap.description}\n view={view}\n items={items}\n />\n <RoadmapHeaderActions roadmapId={roadmap.id} name={roadmap.name} />\n </div>\n <div className=\"flex gap-2\">\n <Badge variant={roadmap.isPublic ? 'default' : 'secondary'} className=\"capitalize\">\n {roadmap.isPublic ? 'public' : 'private'}\n </Badge>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-5 gap-4\">\n {[\n { label: 'Progress', value: `${progress}%` },\n { label: 'Total Items', value: items.length },\n { label: 'Completed', value: completedItems.length },\n { label: 'Planning focus', value: planningFocusLabel(roadmap.planningFocus) },\n { label: 'Visibility', value: roadmap.isPublic ? 'Public' : 'Private' },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4 text-center\">\n <p className=\"text-xs text-muted-foreground\">{s.label}</p>\n <p className=\"text-lg font-semibold capitalize\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div\n className=\"flex items-center gap-2\"\n role=\"group\"\n aria-label=\"Roadmap view\"\n data-testid=\"roadmap-view-toggle\"\n >\n {(\n [\n { key: 'timeline', label: 'Timeline' },\n { key: 'nnl', label: 'Now / Next / Later' },\n ] as const\n ).map((v) => (\n <button\n key={v.key}\n type=\"button\"\n aria-pressed={view === v.key}\n onClick={() => setView(v.key)}\n className={`px-2 py-1 text-xs rounded-md ${view === v.key ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {v.label}\n </button>\n ))}\n </div>\n\n {view === 'nnl' && <RoadmapNowNextLater roadmapId={roadmap.id} />}\n\n {view === 'timeline' && (\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between gap-2\">\n <CardTitle>Progress Overview</CardTitle>\n <RoadmapItemDialog roadmapId={roadmap.id} lanes={lanes} />\n </CardHeader>\n <CardContent>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Overall Completion</span>\n <span className=\"font-medium\">{progress}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full transition-all\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </CardContent>\n </Card>\n\n {items.length === 0 && (\n <p className=\"rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground\">\n This roadmap has no items yet. Use <span className=\"font-medium\">Add item</span> to\n plan the first one.\n </p>\n )}\n {Object.entries(swimlaneGroups).map(([lane, laneItems]) => (\n <Card key={lane}>\n <CardHeader>\n <CardTitle>\n {lane} ({laneItems.length})\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {laneItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n {/* PRM-24: demand behind the initiative, counted by the server. */}\n {item.featureRequestDemand && item.featureRequestDemand.requests > 0 && (\n <p\n className=\"text-xs text-muted-foreground\"\n data-testid=\"roadmap-item-demand\"\n >\n {item.featureRequestDemand.requests} feature request\n {item.featureRequestDemand.requests === 1 ? '' : 's'} ·{' '}\n {item.featureRequestDemand.votes} vote\n {item.featureRequestDemand.votes === 1 ? '' : 's'}\n </p>\n )}\n {item.description && (\n <p className=\"text-xs text-muted-foreground\">\n {item.description.length > 60\n ? `${item.description.slice(0, 60)}…`\n : item.description}\n </p>\n )}\n {(item.startDate || item.endDate) && (\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {item.startDate ? new Date(item.startDate).toLocaleDateString() : '?'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : '?'}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-2\">\n {item.color && (\n <div\n className=\"h-3 w-3 rounded-full\"\n style={{ backgroundColor: item.color }}\n />\n )}\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[item.status] ?? 'bg-gray-100'}`}\n >\n {item.status.toLowerCase().replace('_', ' ')}\n </span>\n {/* PRM-27 — read back from the server; hidden for the common case of an unscored item. */}\n <QuadrantBadge quadrant={item.valueEffortQuadrant} hideWhenUnscored />\n <RoadmapItemCommentsButton\n roadmapId={roadmap.id}\n itemId={item.id}\n itemTitle={item.title}\n />\n <RoadmapItemDialog roadmapId={roadmap.id} item={item} lanes={lanes} />\n <RemoveRoadmapItemButton id={item.id} title={item.title} />\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Planning focus</span>\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n )}\n\n {/* PRM-11: collaboration (discussion, review, versions) is roadmap-level state, not tied to either view\n above — shown regardless of the Timeline/Now-Next-Later toggle. */}\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2\">\n <RoadmapDiscussionPanel roadmapId={roadmap.id} />\n </div>\n <div className=\"space-y-6\">\n <RoadmapReviewPanel roadmapId={roadmap.id} />\n <RoadmapVersionsPanel roadmapId={roadmap.id} />\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"AAmIA,wBAAgB,6BAA6B,4CAskB5C"}
|