@burdenoff/microfe-planmagnet 2026.1001.7 → 2026.1001.8
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/planmagnet/hooks/useLiveRefresh.d.ts +33 -2
- package/dist/planmagnet/hooks/useLiveRefresh.d.ts.map +1 -1
- package/dist/planmagnet/hooks/useLiveRefresh.js +71 -42
- package/dist/planmagnet/hooks/useLiveRefresh.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +153 -117
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +173 -138
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js +171 -153
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,25 +1,27 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import { PersonName as
|
|
3
|
-
import { readUrlParam as
|
|
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 {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
import {
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { PersonName as n } from "../people/People.js";
|
|
3
|
+
import { readUrlParam as r, writeUrlParam as i } from "../utils/urlParam.js";
|
|
4
|
+
import { useLiveRefresh as a } from "../hooks/useLiveRefresh.js";
|
|
5
|
+
import { LiveStatus as o } from "../controls/LiveStatus.js";
|
|
6
|
+
import { PresenceAvatars as s } from "../presence/PresenceAvatars.js";
|
|
7
|
+
import { usePresence as c } from "../hooks/usePresence.js";
|
|
8
|
+
import { PlanningFocusBadge as l, planningFocusLabel as u } from "../roadmaps/planningFocus.js";
|
|
9
|
+
import { QuadrantBadge as d } from "../controls/PrioritizationControls.js";
|
|
10
|
+
import { RemoveRoadmapItemButton as f, RoadmapHeaderActions as p, RoadmapItemDialog as m } from "../roadmaps/RoadmapControls.js";
|
|
11
|
+
import { RoadmapDiscussionPanel as ee, RoadmapItemCommentsButton as h } from "../comments/RoadmapComments.js";
|
|
12
|
+
import { RoadmapNowNextLater as g } from "../roadmaps/RoadmapNowNextLater.js";
|
|
13
|
+
import { RoadmapPresentation as _ } from "../roadmaps/RoadmapPresentation.js";
|
|
14
|
+
import { RoadmapReviewPanel as te } from "../roadmaps/RoadmapReviewPanel.js";
|
|
15
|
+
import { RoadmapVersionsPanel as v } from "../roadmaps/RoadmapVersionsPanel.js";
|
|
16
|
+
import { useState as y } from "react";
|
|
17
|
+
import { Calendar as b, Loader2 as ne, Map as re } from "lucide-react";
|
|
18
|
+
import { useParams as x } from "react-router";
|
|
19
|
+
import { gql as S } from "@apollo/client";
|
|
20
|
+
import { useQuery as C } from "@apollo/client/react";
|
|
21
|
+
import { jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
22
|
+
import { Badge as E, Button as D, Card as O, CardContent as k, CardHeader as A, CardTitle as j } from "@burdenoff/fe-libs/ui";
|
|
21
23
|
//#region src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx
|
|
22
|
-
var M =
|
|
24
|
+
var M = ["GetRoadmapDetailPage"], N = S`
|
|
23
25
|
query GetRoadmapDetailPage($id: ID!) {
|
|
24
26
|
planmagnetRoadmap(id: $id) {
|
|
25
27
|
id
|
|
@@ -60,7 +62,7 @@ var M = C`
|
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
|
-
`,
|
|
65
|
+
`, P = (e) => e === "timeline" || e === "nnl", F = {
|
|
64
66
|
TODO: "bg-gray-100 text-gray-800",
|
|
65
67
|
IN_PROGRESS: "bg-blue-100 text-blue-800",
|
|
66
68
|
DONE: "bg-green-100 text-green-800",
|
|
@@ -68,110 +70,143 @@ var M = C`
|
|
|
68
70
|
BLOCKED: "bg-red-100 text-red-800",
|
|
69
71
|
PLANNED: "bg-yellow-100 text-yellow-800"
|
|
70
72
|
};
|
|
71
|
-
function
|
|
72
|
-
let { workspaceId:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
73
|
+
function I() {
|
|
74
|
+
let { workspaceId: S } = e(), I = x().id ?? "", L = t(), R = c("ROADMAP", I || null), [z, B] = y(!1), V = a({
|
|
75
|
+
kind: "roadmap",
|
|
76
|
+
roadmapId: I,
|
|
77
|
+
queries: M,
|
|
78
|
+
onEvent: (e) => {
|
|
79
|
+
e.kind === "DELETED" && e.id === I && B(!0);
|
|
80
|
+
}
|
|
81
|
+
}), [H, U] = y(() => {
|
|
82
|
+
let e = r("view");
|
|
83
|
+
return P(e) ? e : "timeline";
|
|
84
|
+
}), W = (e) => {
|
|
85
|
+
U(e), i("view", e === "timeline" ? null : e);
|
|
86
|
+
}, { data: G, loading: K, error: q } = C(N, {
|
|
87
|
+
variables: { id: I },
|
|
88
|
+
skip: !I
|
|
80
89
|
});
|
|
81
|
-
if (
|
|
90
|
+
if (z) return /* @__PURE__ */ T("div", {
|
|
91
|
+
className: "space-y-3 p-6",
|
|
92
|
+
"data-testid": "roadmap-deleted",
|
|
93
|
+
children: [/* @__PURE__ */ w("p", {
|
|
94
|
+
className: "text-sm text-muted-foreground",
|
|
95
|
+
children: "This roadmap was deleted."
|
|
96
|
+
}), /* @__PURE__ */ w(D, {
|
|
97
|
+
size: "sm",
|
|
98
|
+
variant: "ghost",
|
|
99
|
+
onClick: () => L("/products/roadmaps-page"),
|
|
100
|
+
children: "Back to roadmaps"
|
|
101
|
+
})]
|
|
102
|
+
});
|
|
103
|
+
if (K && !G) return /* @__PURE__ */ w("div", {
|
|
82
104
|
className: "flex items-center justify-center p-12",
|
|
83
|
-
children: /* @__PURE__ */
|
|
105
|
+
children: /* @__PURE__ */ w(ne, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
84
106
|
});
|
|
85
|
-
if (
|
|
107
|
+
if (q) return /* @__PURE__ */ T("div", {
|
|
86
108
|
className: "p-6 text-destructive",
|
|
87
|
-
children: ["Failed to load roadmap: ",
|
|
109
|
+
children: ["Failed to load roadmap: ", q.message]
|
|
88
110
|
});
|
|
89
|
-
let
|
|
90
|
-
if (!
|
|
91
|
-
className: "p-6
|
|
92
|
-
|
|
111
|
+
let J = G?.planmagnetRoadmap;
|
|
112
|
+
if (!J) return /* @__PURE__ */ T("div", {
|
|
113
|
+
className: "space-y-3 p-6",
|
|
114
|
+
"data-testid": "roadmap-not-found",
|
|
115
|
+
children: [/* @__PURE__ */ w("p", {
|
|
116
|
+
className: "text-sm text-muted-foreground",
|
|
117
|
+
children: "This roadmap does not exist in this workspace, or it was deleted."
|
|
118
|
+
}), /* @__PURE__ */ w(D, {
|
|
119
|
+
size: "sm",
|
|
120
|
+
variant: "ghost",
|
|
121
|
+
onClick: () => L("/products/roadmaps-page"),
|
|
122
|
+
children: "Back to roadmaps"
|
|
123
|
+
})]
|
|
93
124
|
});
|
|
94
|
-
let
|
|
95
|
-
for (let e of
|
|
125
|
+
let Y = (J.items ?? []).slice().sort((e, t) => e.sortOrder - t.sortOrder), X = Y.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), Z = Y.length > 0 ? Math.round(X.length / Y.length * 100) : 0, Q = [...new Set(Y.map((e) => e.swimlane).filter(Boolean))], $ = {};
|
|
126
|
+
for (let e of Y) {
|
|
96
127
|
let t = e.swimlane ?? "Uncategorized";
|
|
97
|
-
|
|
128
|
+
$[t] || ($[t] = []), $[t].push(e);
|
|
98
129
|
}
|
|
99
|
-
return /* @__PURE__ */
|
|
130
|
+
return /* @__PURE__ */ T("div", {
|
|
100
131
|
className: "space-y-6 p-6",
|
|
101
132
|
children: [
|
|
102
|
-
/* @__PURE__ */
|
|
133
|
+
/* @__PURE__ */ T("div", {
|
|
103
134
|
className: "flex items-center justify-between",
|
|
104
|
-
children: [/* @__PURE__ */
|
|
105
|
-
/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ T("div", { children: [
|
|
136
|
+
/* @__PURE__ */ T("div", {
|
|
106
137
|
className: "flex items-center gap-2",
|
|
107
|
-
children: [
|
|
108
|
-
className: "
|
|
109
|
-
|
|
110
|
-
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ w(re, { className: "h-5 w-5 text-primary" }),
|
|
140
|
+
/* @__PURE__ */ w("h1", {
|
|
141
|
+
className: "text-2xl font-semibold",
|
|
142
|
+
children: J.name
|
|
143
|
+
}),
|
|
144
|
+
/* @__PURE__ */ w(o, { mode: V.mode })
|
|
145
|
+
]
|
|
111
146
|
}),
|
|
112
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ w("p", {
|
|
113
148
|
className: "text-sm text-muted-foreground mt-1",
|
|
114
|
-
children:
|
|
149
|
+
children: J.description ?? "No description"
|
|
115
150
|
}),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
-
] }), /* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ w(s, { others: R.others })
|
|
152
|
+
] }), /* @__PURE__ */ T("div", {
|
|
118
153
|
className: "flex flex-col items-end gap-2",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
154
|
+
children: [/* @__PURE__ */ T("div", {
|
|
120
155
|
className: "flex items-center gap-2",
|
|
121
|
-
children: [/* @__PURE__ */
|
|
122
|
-
roadmapName:
|
|
123
|
-
roadmapDescription:
|
|
124
|
-
view:
|
|
125
|
-
items:
|
|
126
|
-
}), /* @__PURE__ */
|
|
127
|
-
roadmapId:
|
|
128
|
-
name:
|
|
156
|
+
children: [/* @__PURE__ */ w(_, {
|
|
157
|
+
roadmapName: J.name,
|
|
158
|
+
roadmapDescription: J.description,
|
|
159
|
+
view: H,
|
|
160
|
+
items: Y
|
|
161
|
+
}), /* @__PURE__ */ w(p, {
|
|
162
|
+
roadmapId: J.id,
|
|
163
|
+
name: J.name
|
|
129
164
|
})]
|
|
130
|
-
}), /* @__PURE__ */
|
|
165
|
+
}), /* @__PURE__ */ T("div", {
|
|
131
166
|
className: "flex gap-2",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
133
|
-
variant:
|
|
167
|
+
children: [/* @__PURE__ */ w(E, {
|
|
168
|
+
variant: J.isPublic ? "default" : "secondary",
|
|
134
169
|
className: "capitalize",
|
|
135
|
-
children:
|
|
136
|
-
}), /* @__PURE__ */
|
|
170
|
+
children: J.isPublic ? "public" : "private"
|
|
171
|
+
}), /* @__PURE__ */ w(l, { focus: J.planningFocus })]
|
|
137
172
|
})]
|
|
138
173
|
})]
|
|
139
174
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
175
|
+
/* @__PURE__ */ w("div", {
|
|
141
176
|
className: "grid grid-cols-2 md:grid-cols-5 gap-4",
|
|
142
177
|
children: [
|
|
143
178
|
{
|
|
144
179
|
label: "Progress",
|
|
145
|
-
value: `${
|
|
180
|
+
value: `${Z}%`
|
|
146
181
|
},
|
|
147
182
|
{
|
|
148
183
|
label: "Total Items",
|
|
149
|
-
value:
|
|
184
|
+
value: Y.length
|
|
150
185
|
},
|
|
151
186
|
{
|
|
152
187
|
label: "Completed",
|
|
153
|
-
value:
|
|
188
|
+
value: X.length
|
|
154
189
|
},
|
|
155
190
|
{
|
|
156
191
|
label: "Planning focus",
|
|
157
|
-
value:
|
|
192
|
+
value: u(J.planningFocus)
|
|
158
193
|
},
|
|
159
194
|
{
|
|
160
195
|
label: "Visibility",
|
|
161
|
-
value:
|
|
196
|
+
value: J.isPublic ? "Public" : "Private"
|
|
162
197
|
}
|
|
163
|
-
].map((e) => /* @__PURE__ */
|
|
198
|
+
].map((e) => /* @__PURE__ */ w(O, { children: /* @__PURE__ */ T(k, {
|
|
164
199
|
className: "pt-4 text-center",
|
|
165
|
-
children: [/* @__PURE__ */
|
|
200
|
+
children: [/* @__PURE__ */ w("p", {
|
|
166
201
|
className: "text-xs text-muted-foreground",
|
|
167
202
|
children: e.label
|
|
168
|
-
}), /* @__PURE__ */
|
|
203
|
+
}), /* @__PURE__ */ w("p", {
|
|
169
204
|
className: "text-lg font-semibold capitalize",
|
|
170
205
|
children: e.value
|
|
171
206
|
})]
|
|
172
207
|
}) }, e.label))
|
|
173
208
|
}),
|
|
174
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ w("div", {
|
|
175
210
|
className: "flex items-center gap-2",
|
|
176
211
|
role: "group",
|
|
177
212
|
"aria-label": "Roadmap view",
|
|
@@ -182,68 +217,68 @@ function F() {
|
|
|
182
217
|
}, {
|
|
183
218
|
key: "nnl",
|
|
184
219
|
label: "Now / Next / Later"
|
|
185
|
-
}].map((e) => /* @__PURE__ */
|
|
220
|
+
}].map((e) => /* @__PURE__ */ w("button", {
|
|
186
221
|
type: "button",
|
|
187
|
-
"aria-pressed":
|
|
188
|
-
onClick: () =>
|
|
189
|
-
className: `px-2 py-1 text-xs rounded-md ${
|
|
222
|
+
"aria-pressed": H === e.key,
|
|
223
|
+
onClick: () => W(e.key),
|
|
224
|
+
className: `px-2 py-1 text-xs rounded-md ${H === e.key ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
190
225
|
children: e.label
|
|
191
226
|
}, e.key))
|
|
192
227
|
}),
|
|
193
|
-
|
|
194
|
-
|
|
228
|
+
H === "nnl" && /* @__PURE__ */ w(g, { roadmapId: J.id }),
|
|
229
|
+
H === "timeline" && /* @__PURE__ */ T("div", {
|
|
195
230
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
196
|
-
children: [/* @__PURE__ */
|
|
231
|
+
children: [/* @__PURE__ */ T("div", {
|
|
197
232
|
className: "lg:col-span-2 space-y-6",
|
|
198
233
|
children: [
|
|
199
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ T(O, { children: [/* @__PURE__ */ T(A, {
|
|
200
235
|
className: "flex flex-row items-center justify-between gap-2",
|
|
201
|
-
children: [/* @__PURE__ */
|
|
202
|
-
roadmapId:
|
|
203
|
-
lanes:
|
|
236
|
+
children: [/* @__PURE__ */ w(j, { children: "Progress Overview" }), /* @__PURE__ */ w(m, {
|
|
237
|
+
roadmapId: J.id,
|
|
238
|
+
lanes: Q
|
|
204
239
|
})]
|
|
205
|
-
}), /* @__PURE__ */
|
|
240
|
+
}), /* @__PURE__ */ T(k, { children: [/* @__PURE__ */ T("div", {
|
|
206
241
|
className: "flex justify-between text-sm mb-2",
|
|
207
|
-
children: [/* @__PURE__ */
|
|
242
|
+
children: [/* @__PURE__ */ w("span", {
|
|
208
243
|
className: "text-muted-foreground",
|
|
209
244
|
children: "Overall Completion"
|
|
210
|
-
}), /* @__PURE__ */
|
|
245
|
+
}), /* @__PURE__ */ T("span", {
|
|
211
246
|
className: "font-medium",
|
|
212
|
-
children: [
|
|
247
|
+
children: [Z, "%"]
|
|
213
248
|
})]
|
|
214
|
-
}), /* @__PURE__ */
|
|
249
|
+
}), /* @__PURE__ */ w("div", {
|
|
215
250
|
className: "w-full h-4 rounded-full bg-muted overflow-hidden",
|
|
216
|
-
children: /* @__PURE__ */
|
|
251
|
+
children: /* @__PURE__ */ w("div", {
|
|
217
252
|
className: "h-full bg-primary rounded-full transition-all",
|
|
218
|
-
style: { width: `${
|
|
253
|
+
style: { width: `${Z}%` }
|
|
219
254
|
})
|
|
220
255
|
})] })] }),
|
|
221
|
-
|
|
256
|
+
Y.length === 0 && /* @__PURE__ */ T("p", {
|
|
222
257
|
className: "rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground",
|
|
223
258
|
children: [
|
|
224
259
|
"This roadmap has no items yet. Use ",
|
|
225
|
-
/* @__PURE__ */
|
|
260
|
+
/* @__PURE__ */ w("span", {
|
|
226
261
|
className: "font-medium",
|
|
227
262
|
children: "Add item"
|
|
228
263
|
}),
|
|
229
264
|
" to plan the first one."
|
|
230
265
|
]
|
|
231
266
|
}),
|
|
232
|
-
Object.entries(
|
|
267
|
+
Object.entries($).map(([e, t]) => /* @__PURE__ */ T(O, { children: [/* @__PURE__ */ w(A, { children: /* @__PURE__ */ T(j, { children: [
|
|
233
268
|
e,
|
|
234
269
|
" (",
|
|
235
270
|
t.length,
|
|
236
271
|
")"
|
|
237
|
-
] }) }), /* @__PURE__ */
|
|
272
|
+
] }) }), /* @__PURE__ */ w(k, {
|
|
238
273
|
className: "space-y-3",
|
|
239
|
-
children: t.map((e) => /* @__PURE__ */
|
|
274
|
+
children: t.map((e) => /* @__PURE__ */ T("div", {
|
|
240
275
|
className: "flex items-center justify-between border rounded-md p-3",
|
|
241
|
-
children: [/* @__PURE__ */
|
|
242
|
-
/* @__PURE__ */
|
|
276
|
+
children: [/* @__PURE__ */ T("div", { children: [
|
|
277
|
+
/* @__PURE__ */ w("p", {
|
|
243
278
|
className: "text-sm font-medium",
|
|
244
279
|
children: e.title
|
|
245
280
|
}),
|
|
246
|
-
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */
|
|
281
|
+
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */ T("p", {
|
|
247
282
|
className: "text-xs text-muted-foreground",
|
|
248
283
|
"data-testid": "roadmap-item-demand",
|
|
249
284
|
children: [
|
|
@@ -257,45 +292,45 @@ function F() {
|
|
|
257
292
|
e.featureRequestDemand.votes === 1 ? "" : "s"
|
|
258
293
|
]
|
|
259
294
|
}),
|
|
260
|
-
e.description && /* @__PURE__ */
|
|
295
|
+
e.description && /* @__PURE__ */ w("p", {
|
|
261
296
|
className: "text-xs text-muted-foreground",
|
|
262
297
|
children: e.description.length > 60 ? `${e.description.slice(0, 60)}…` : e.description
|
|
263
298
|
}),
|
|
264
|
-
(e.startDate || e.endDate) && /* @__PURE__ */
|
|
299
|
+
(e.startDate || e.endDate) && /* @__PURE__ */ T("div", {
|
|
265
300
|
className: "flex items-center gap-1 mt-1 text-xs text-muted-foreground",
|
|
266
301
|
children: [
|
|
267
|
-
/* @__PURE__ */
|
|
302
|
+
/* @__PURE__ */ w(b, { className: "h-3 w-3" }),
|
|
268
303
|
e.startDate ? new Date(e.startDate).toLocaleDateString() : "?",
|
|
269
304
|
" - ",
|
|
270
305
|
e.endDate ? new Date(e.endDate).toLocaleDateString() : "?"
|
|
271
306
|
]
|
|
272
307
|
})
|
|
273
|
-
] }), /* @__PURE__ */
|
|
308
|
+
] }), /* @__PURE__ */ T("div", {
|
|
274
309
|
className: "flex items-center gap-2",
|
|
275
310
|
children: [
|
|
276
|
-
e.color && /* @__PURE__ */
|
|
311
|
+
e.color && /* @__PURE__ */ w("div", {
|
|
277
312
|
className: "h-3 w-3 rounded-full",
|
|
278
313
|
style: { backgroundColor: e.color }
|
|
279
314
|
}),
|
|
280
|
-
/* @__PURE__ */
|
|
281
|
-
className: `text-xs px-2 py-0.5 rounded-full capitalize ${
|
|
315
|
+
/* @__PURE__ */ w("span", {
|
|
316
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${F[e.status] ?? "bg-gray-100"}`,
|
|
282
317
|
children: e.status.toLowerCase().replace("_", " ")
|
|
283
318
|
}),
|
|
284
|
-
/* @__PURE__ */
|
|
319
|
+
/* @__PURE__ */ w(d, {
|
|
285
320
|
quadrant: e.valueEffortQuadrant,
|
|
286
321
|
hideWhenUnscored: !0
|
|
287
322
|
}),
|
|
288
|
-
/* @__PURE__ */
|
|
289
|
-
roadmapId:
|
|
323
|
+
/* @__PURE__ */ w(h, {
|
|
324
|
+
roadmapId: J.id,
|
|
290
325
|
itemId: e.id,
|
|
291
326
|
itemTitle: e.title
|
|
292
327
|
}),
|
|
293
|
-
/* @__PURE__ */
|
|
294
|
-
roadmapId:
|
|
328
|
+
/* @__PURE__ */ w(m, {
|
|
329
|
+
roadmapId: J.id,
|
|
295
330
|
item: e,
|
|
296
|
-
lanes:
|
|
331
|
+
lanes: Q
|
|
297
332
|
}),
|
|
298
|
-
/* @__PURE__ */
|
|
333
|
+
/* @__PURE__ */ w(f, {
|
|
299
334
|
id: e.id,
|
|
300
335
|
title: e.title
|
|
301
336
|
})
|
|
@@ -304,57 +339,57 @@ function F() {
|
|
|
304
339
|
}, e.id))
|
|
305
340
|
})] }, e))
|
|
306
341
|
]
|
|
307
|
-
}), /* @__PURE__ */
|
|
342
|
+
}), /* @__PURE__ */ w("div", {
|
|
308
343
|
className: "space-y-6",
|
|
309
|
-
children: /* @__PURE__ */
|
|
344
|
+
children: /* @__PURE__ */ T(O, { children: [/* @__PURE__ */ w(A, { children: /* @__PURE__ */ w(j, { children: "Details" }) }), /* @__PURE__ */ T(k, {
|
|
310
345
|
className: "space-y-2 text-sm",
|
|
311
346
|
children: [
|
|
312
|
-
/* @__PURE__ */
|
|
347
|
+
/* @__PURE__ */ T("div", {
|
|
313
348
|
className: "flex justify-between",
|
|
314
|
-
children: [/* @__PURE__ */
|
|
349
|
+
children: [/* @__PURE__ */ w("span", {
|
|
315
350
|
className: "text-muted-foreground",
|
|
316
351
|
children: "Planning focus"
|
|
317
|
-
}), /* @__PURE__ */
|
|
352
|
+
}), /* @__PURE__ */ w(l, { focus: J.planningFocus })]
|
|
318
353
|
}),
|
|
319
|
-
/* @__PURE__ */
|
|
354
|
+
/* @__PURE__ */ T("div", {
|
|
320
355
|
className: "flex justify-between",
|
|
321
|
-
children: [/* @__PURE__ */
|
|
356
|
+
children: [/* @__PURE__ */ w("span", {
|
|
322
357
|
className: "text-muted-foreground",
|
|
323
358
|
children: "Owner"
|
|
324
|
-
}), /* @__PURE__ */
|
|
359
|
+
}), /* @__PURE__ */ w("span", { children: /* @__PURE__ */ w(n, { id: J.ownerId }) })]
|
|
325
360
|
}),
|
|
326
|
-
/* @__PURE__ */
|
|
361
|
+
/* @__PURE__ */ T("div", {
|
|
327
362
|
className: "flex justify-between",
|
|
328
|
-
children: [/* @__PURE__ */
|
|
363
|
+
children: [/* @__PURE__ */ w("span", {
|
|
329
364
|
className: "text-muted-foreground",
|
|
330
365
|
children: "Created"
|
|
331
|
-
}), /* @__PURE__ */
|
|
366
|
+
}), /* @__PURE__ */ w("span", { children: new Date(J.createdAt).toLocaleDateString() })]
|
|
332
367
|
}),
|
|
333
|
-
/* @__PURE__ */
|
|
368
|
+
/* @__PURE__ */ T("div", {
|
|
334
369
|
className: "flex justify-between",
|
|
335
|
-
children: [/* @__PURE__ */
|
|
370
|
+
children: [/* @__PURE__ */ w("span", {
|
|
336
371
|
className: "text-muted-foreground",
|
|
337
372
|
children: "Updated"
|
|
338
|
-
}), /* @__PURE__ */
|
|
373
|
+
}), /* @__PURE__ */ w("span", { children: new Date(J.updatedAt).toLocaleDateString() })]
|
|
339
374
|
})
|
|
340
375
|
]
|
|
341
376
|
})] })
|
|
342
377
|
})]
|
|
343
378
|
}),
|
|
344
|
-
/* @__PURE__ */
|
|
379
|
+
/* @__PURE__ */ T("div", {
|
|
345
380
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
346
|
-
children: [/* @__PURE__ */
|
|
381
|
+
children: [/* @__PURE__ */ w("div", {
|
|
347
382
|
className: "lg:col-span-2",
|
|
348
|
-
children: /* @__PURE__ */
|
|
349
|
-
}), /* @__PURE__ */
|
|
383
|
+
children: /* @__PURE__ */ w(ee, { roadmapId: J.id })
|
|
384
|
+
}), /* @__PURE__ */ T("div", {
|
|
350
385
|
className: "space-y-6",
|
|
351
|
-
children: [/* @__PURE__ */
|
|
386
|
+
children: [/* @__PURE__ */ w(te, { roadmapId: J.id }), /* @__PURE__ */ w(v, { roadmapId: J.id })]
|
|
352
387
|
})]
|
|
353
388
|
})
|
|
354
389
|
]
|
|
355
390
|
});
|
|
356
391
|
}
|
|
357
392
|
//#endregion
|
|
358
|
-
export {
|
|
393
|
+
export { I as ProductsRoadmapDetailPagePage };
|
|
359
394
|
|
|
360
395
|
//# 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';\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
|
+
{"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 { Button, Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Map, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } 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';\nimport { useLiveRefresh, type LiveEvent } from '../hooks/useLiveRefresh';\nimport { LiveStatus } from '../controls/LiveStatus';\n\n// PRM-11: this page's one query — re-read on any roadmap- or item-level event (`useLiveRefresh` below), same\n// \"one query, one refetch\" shape `PROJECT_DETAIL_QUERIES` uses on the project detail page.\nconst ROADMAP_DETAIL_QUERIES = ['GetRoadmapDetailPage'];\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 const navigate = usePlanMagnetNavigate();\n // CWF-24: who else is looking at this roadmap right now.\n const presence = usePresence('ROADMAP', roadmapId || null);\n // PRM-11: once a DELETED event names the roadmap itself (`event.id === roadmapId` — not one of its items, see\n // `LiveEvent.roadmapId`), there is nothing left to refetch: jump straight to the clear \"This roadmap was\n // deleted\" state below instead of leaving the page on stale data until a refetch eventually confirms it (which\n // can lag the event by up to the debounce window), or racing a refetch against a record already gone.\n const [roadmapDeleted, setRoadmapDeleted] = useState(false);\n const roadmapLive = useLiveRefresh({\n kind: 'roadmap',\n roadmapId,\n queries: ROADMAP_DETAIL_QUERIES,\n onEvent: (event: LiveEvent) => {\n if (event.kind === 'DELETED' && event.id === roadmapId) setRoadmapDeleted(true);\n },\n });\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 // PRM-11: authoritative the moment the live event says so — ahead of the loading/error checks below, which\n // only resolve once their own (possibly still in-flight, possibly debounced) refetch lands.\n if (roadmapDeleted) {\n return (\n <div className=\"space-y-3 p-6\" data-testid=\"roadmap-deleted\">\n <p className=\"text-sm text-muted-foreground\">This roadmap was deleted.</p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/products/roadmaps-page')}>\n Back to roadmaps\n </Button>\n </div>\n );\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 // Belt-and-braces fallback for a roadmap that was already gone before this page's own subscription ever saw a\n // DELETED event (e.g. deleted while this tab was on the polling fallback, or opened on an already-dead link) —\n // same \"does not exist, or it was deleted\" wording `ProjectsBudgetDetailPagePage`/`DocsDocDetailPagePage` use.\n if (!roadmap) {\n return (\n <div className=\"space-y-3 p-6\" data-testid=\"roadmap-not-found\">\n <p className=\"text-sm text-muted-foreground\">\n This roadmap does not exist in this workspace, or it was deleted.\n </p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/products/roadmaps-page')}>\n Back to roadmaps\n </Button>\n </div>\n );\n }\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 <LiveStatus mode={roadmapLive.mode} />\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":"RoadmapNowNextLater.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/roadmaps/RoadmapNowNextLater.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RoadmapNowNextLater.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/roadmaps/RoadmapNowNextLater.tsx"],"names":[],"mappings":"AA4FA,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oBAAoB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClE;AA4ID;;qHAEqH;AACrH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,IAAI,GACL,EAAE;IACD,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,kDA4BA;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,kDAW5E;AA0JD,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,2CAmYvE"}
|