@burdenoff/microfe-planmagnet 2026.930.23 → 2026.930.24
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/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/index.d.ts +1 -0
- package/dist/planmagnet/index.d.ts.map +1 -1
- package/dist/planmagnet/index.js +1 -0
- 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 +133 -123
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- 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/package.json +1 -1
|
@@ -2,21 +2,22 @@ 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
4
|
import { PlanningFocusBadge as i, planningFocusLabel as a } from "../roadmaps/planningFocus.js";
|
|
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 {
|
|
5
|
+
import { QuadrantBadge as o } from "../controls/PrioritizationControls.js";
|
|
6
|
+
import { RemoveRoadmapItemButton as s, RoadmapHeaderActions as c, RoadmapItemDialog as l } from "../roadmaps/RoadmapControls.js";
|
|
7
|
+
import { RoadmapDiscussionPanel as u, RoadmapItemCommentsButton as d } from "../comments/RoadmapComments.js";
|
|
8
|
+
import { RoadmapNowNextLater as f } from "../roadmaps/RoadmapNowNextLater.js";
|
|
9
|
+
import { RoadmapPresentation as p } from "../roadmaps/RoadmapPresentation.js";
|
|
10
|
+
import { RoadmapReviewPanel as m } from "../roadmaps/RoadmapReviewPanel.js";
|
|
11
|
+
import { RoadmapVersionsPanel as h } from "../roadmaps/RoadmapVersionsPanel.js";
|
|
12
|
+
import { useState as g } from "react";
|
|
13
|
+
import { Calendar as _, Loader2 as v, Map as y } from "lucide-react";
|
|
14
|
+
import { useParams as b } from "react-router";
|
|
15
|
+
import { gql as x } from "@apollo/client";
|
|
16
|
+
import { useQuery as S } from "@apollo/client/react";
|
|
17
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
18
|
+
import { Badge as T, Card as E, CardContent as D, CardHeader as O, CardTitle as k } from "@burdenoff/fe-libs/ui";
|
|
18
19
|
//#region src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx
|
|
19
|
-
var
|
|
20
|
+
var A = x`
|
|
20
21
|
query GetRoadmapDetailPage($id: ID!) {
|
|
21
22
|
planmagnetRoadmap(id: $id) {
|
|
22
23
|
id
|
|
@@ -45,6 +46,11 @@ var k = b`
|
|
|
45
46
|
confidence
|
|
46
47
|
riskLevel
|
|
47
48
|
riskNote
|
|
49
|
+
moscow
|
|
50
|
+
kano
|
|
51
|
+
valueScore
|
|
52
|
+
effortScore
|
|
53
|
+
valueEffortQuadrant
|
|
48
54
|
featureRequestDemand {
|
|
49
55
|
requests
|
|
50
56
|
votes
|
|
@@ -52,7 +58,7 @@ var k = b`
|
|
|
52
58
|
}
|
|
53
59
|
}
|
|
54
60
|
}
|
|
55
|
-
`,
|
|
61
|
+
`, j = (e) => e === "timeline" || e === "nnl", M = {
|
|
56
62
|
TODO: "bg-gray-100 text-gray-800",
|
|
57
63
|
IN_PROGRESS: "bg-blue-100 text-blue-800",
|
|
58
64
|
DONE: "bg-green-100 text-green-800",
|
|
@@ -60,106 +66,106 @@ var k = b`
|
|
|
60
66
|
BLOCKED: "bg-red-100 text-red-800",
|
|
61
67
|
PLANNED: "bg-yellow-100 text-yellow-800"
|
|
62
68
|
};
|
|
63
|
-
function
|
|
64
|
-
let { workspaceId:
|
|
69
|
+
function N() {
|
|
70
|
+
let { workspaceId: x } = e(), N = b().id ?? "", [P, F] = g(() => {
|
|
65
71
|
let e = n("view");
|
|
66
|
-
return
|
|
67
|
-
}),
|
|
68
|
-
|
|
69
|
-
}, { data:
|
|
70
|
-
variables: { id:
|
|
71
|
-
skip: !
|
|
72
|
+
return j(e) ? e : "timeline";
|
|
73
|
+
}), I = (e) => {
|
|
74
|
+
F(e), r("view", e === "timeline" ? null : e);
|
|
75
|
+
}, { data: L, loading: R, error: z } = S(A, {
|
|
76
|
+
variables: { id: N },
|
|
77
|
+
skip: !N
|
|
72
78
|
});
|
|
73
|
-
if (
|
|
79
|
+
if (R && !L) return /* @__PURE__ */ C("div", {
|
|
74
80
|
className: "flex items-center justify-center p-12",
|
|
75
|
-
children: /* @__PURE__ */
|
|
81
|
+
children: /* @__PURE__ */ C(v, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
76
82
|
});
|
|
77
|
-
if (
|
|
83
|
+
if (z) return /* @__PURE__ */ w("div", {
|
|
78
84
|
className: "p-6 text-destructive",
|
|
79
|
-
children: ["Failed to load roadmap: ",
|
|
85
|
+
children: ["Failed to load roadmap: ", z.message]
|
|
80
86
|
});
|
|
81
|
-
let
|
|
82
|
-
if (!
|
|
87
|
+
let B = L?.planmagnetRoadmap;
|
|
88
|
+
if (!B) return /* @__PURE__ */ C("div", {
|
|
83
89
|
className: "p-6 text-muted-foreground",
|
|
84
90
|
children: "Roadmap not found."
|
|
85
91
|
});
|
|
86
|
-
let
|
|
87
|
-
for (let e of
|
|
92
|
+
let V = (B.items ?? []).slice().sort((e, t) => e.sortOrder - t.sortOrder), H = V.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), U = V.length > 0 ? Math.round(H.length / V.length * 100) : 0, W = [...new Set(V.map((e) => e.swimlane).filter(Boolean))], G = {};
|
|
93
|
+
for (let e of V) {
|
|
88
94
|
let t = e.swimlane ?? "Uncategorized";
|
|
89
|
-
|
|
95
|
+
G[t] || (G[t] = []), G[t].push(e);
|
|
90
96
|
}
|
|
91
|
-
return /* @__PURE__ */
|
|
97
|
+
return /* @__PURE__ */ w("div", {
|
|
92
98
|
className: "space-y-6 p-6",
|
|
93
99
|
children: [
|
|
94
|
-
/* @__PURE__ */
|
|
100
|
+
/* @__PURE__ */ w("div", {
|
|
95
101
|
className: "flex items-center justify-between",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
102
|
+
children: [/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ w("div", {
|
|
97
103
|
className: "flex items-center gap-2",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
104
|
+
children: [/* @__PURE__ */ C(y, { className: "h-5 w-5 text-primary" }), /* @__PURE__ */ C("h1", {
|
|
99
105
|
className: "text-2xl font-semibold",
|
|
100
|
-
children:
|
|
106
|
+
children: B.name
|
|
101
107
|
})]
|
|
102
|
-
}), /* @__PURE__ */
|
|
108
|
+
}), /* @__PURE__ */ C("p", {
|
|
103
109
|
className: "text-sm text-muted-foreground mt-1",
|
|
104
|
-
children:
|
|
105
|
-
})] }), /* @__PURE__ */
|
|
110
|
+
children: B.description ?? "No description"
|
|
111
|
+
})] }), /* @__PURE__ */ w("div", {
|
|
106
112
|
className: "flex flex-col items-end gap-2",
|
|
107
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ w("div", {
|
|
108
114
|
className: "flex items-center gap-2",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
110
|
-
roadmapName:
|
|
111
|
-
roadmapDescription:
|
|
112
|
-
view:
|
|
113
|
-
items:
|
|
114
|
-
}), /* @__PURE__ */
|
|
115
|
-
roadmapId:
|
|
116
|
-
name:
|
|
115
|
+
children: [/* @__PURE__ */ C(p, {
|
|
116
|
+
roadmapName: B.name,
|
|
117
|
+
roadmapDescription: B.description,
|
|
118
|
+
view: P,
|
|
119
|
+
items: V
|
|
120
|
+
}), /* @__PURE__ */ C(c, {
|
|
121
|
+
roadmapId: B.id,
|
|
122
|
+
name: B.name
|
|
117
123
|
})]
|
|
118
|
-
}), /* @__PURE__ */
|
|
124
|
+
}), /* @__PURE__ */ w("div", {
|
|
119
125
|
className: "flex gap-2",
|
|
120
|
-
children: [/* @__PURE__ */
|
|
121
|
-
variant:
|
|
126
|
+
children: [/* @__PURE__ */ C(T, {
|
|
127
|
+
variant: B.isPublic ? "default" : "secondary",
|
|
122
128
|
className: "capitalize",
|
|
123
|
-
children:
|
|
124
|
-
}), /* @__PURE__ */
|
|
129
|
+
children: B.isPublic ? "public" : "private"
|
|
130
|
+
}), /* @__PURE__ */ C(i, { focus: B.planningFocus })]
|
|
125
131
|
})]
|
|
126
132
|
})]
|
|
127
133
|
}),
|
|
128
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ C("div", {
|
|
129
135
|
className: "grid grid-cols-2 md:grid-cols-5 gap-4",
|
|
130
136
|
children: [
|
|
131
137
|
{
|
|
132
138
|
label: "Progress",
|
|
133
|
-
value: `${
|
|
139
|
+
value: `${U}%`
|
|
134
140
|
},
|
|
135
141
|
{
|
|
136
142
|
label: "Total Items",
|
|
137
|
-
value:
|
|
143
|
+
value: V.length
|
|
138
144
|
},
|
|
139
145
|
{
|
|
140
146
|
label: "Completed",
|
|
141
|
-
value:
|
|
147
|
+
value: H.length
|
|
142
148
|
},
|
|
143
149
|
{
|
|
144
150
|
label: "Planning focus",
|
|
145
|
-
value: a(
|
|
151
|
+
value: a(B.planningFocus)
|
|
146
152
|
},
|
|
147
153
|
{
|
|
148
154
|
label: "Visibility",
|
|
149
|
-
value:
|
|
155
|
+
value: B.isPublic ? "Public" : "Private"
|
|
150
156
|
}
|
|
151
|
-
].map((e) => /* @__PURE__ */
|
|
157
|
+
].map((e) => /* @__PURE__ */ C(E, { children: /* @__PURE__ */ w(D, {
|
|
152
158
|
className: "pt-4 text-center",
|
|
153
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ C("p", {
|
|
154
160
|
className: "text-xs text-muted-foreground",
|
|
155
161
|
children: e.label
|
|
156
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ C("p", {
|
|
157
163
|
className: "text-lg font-semibold capitalize",
|
|
158
164
|
children: e.value
|
|
159
165
|
})]
|
|
160
166
|
}) }, e.label))
|
|
161
167
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ C("div", {
|
|
163
169
|
className: "flex items-center gap-2",
|
|
164
170
|
role: "group",
|
|
165
171
|
"aria-label": "Roadmap view",
|
|
@@ -170,68 +176,68 @@ function M() {
|
|
|
170
176
|
}, {
|
|
171
177
|
key: "nnl",
|
|
172
178
|
label: "Now / Next / Later"
|
|
173
|
-
}].map((e) => /* @__PURE__ */
|
|
179
|
+
}].map((e) => /* @__PURE__ */ C("button", {
|
|
174
180
|
type: "button",
|
|
175
|
-
"aria-pressed":
|
|
176
|
-
onClick: () =>
|
|
177
|
-
className: `px-2 py-1 text-xs rounded-md ${
|
|
181
|
+
"aria-pressed": P === e.key,
|
|
182
|
+
onClick: () => I(e.key),
|
|
183
|
+
className: `px-2 py-1 text-xs rounded-md ${P === e.key ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
178
184
|
children: e.label
|
|
179
185
|
}, e.key))
|
|
180
186
|
}),
|
|
181
|
-
|
|
182
|
-
|
|
187
|
+
P === "nnl" && /* @__PURE__ */ C(f, { roadmapId: B.id }),
|
|
188
|
+
P === "timeline" && /* @__PURE__ */ w("div", {
|
|
183
189
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
184
|
-
children: [/* @__PURE__ */
|
|
190
|
+
children: [/* @__PURE__ */ w("div", {
|
|
185
191
|
className: "lg:col-span-2 space-y-6",
|
|
186
192
|
children: [
|
|
187
|
-
/* @__PURE__ */
|
|
193
|
+
/* @__PURE__ */ w(E, { children: [/* @__PURE__ */ w(O, {
|
|
188
194
|
className: "flex flex-row items-center justify-between gap-2",
|
|
189
|
-
children: [/* @__PURE__ */
|
|
190
|
-
roadmapId:
|
|
191
|
-
lanes:
|
|
195
|
+
children: [/* @__PURE__ */ C(k, { children: "Progress Overview" }), /* @__PURE__ */ C(l, {
|
|
196
|
+
roadmapId: B.id,
|
|
197
|
+
lanes: W
|
|
192
198
|
})]
|
|
193
|
-
}), /* @__PURE__ */
|
|
199
|
+
}), /* @__PURE__ */ w(D, { children: [/* @__PURE__ */ w("div", {
|
|
194
200
|
className: "flex justify-between text-sm mb-2",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ C("span", {
|
|
196
202
|
className: "text-muted-foreground",
|
|
197
203
|
children: "Overall Completion"
|
|
198
|
-
}), /* @__PURE__ */
|
|
204
|
+
}), /* @__PURE__ */ w("span", {
|
|
199
205
|
className: "font-medium",
|
|
200
|
-
children: [
|
|
206
|
+
children: [U, "%"]
|
|
201
207
|
})]
|
|
202
|
-
}), /* @__PURE__ */
|
|
208
|
+
}), /* @__PURE__ */ C("div", {
|
|
203
209
|
className: "w-full h-4 rounded-full bg-muted overflow-hidden",
|
|
204
|
-
children: /* @__PURE__ */
|
|
210
|
+
children: /* @__PURE__ */ C("div", {
|
|
205
211
|
className: "h-full bg-primary rounded-full transition-all",
|
|
206
|
-
style: { width: `${
|
|
212
|
+
style: { width: `${U}%` }
|
|
207
213
|
})
|
|
208
214
|
})] })] }),
|
|
209
|
-
|
|
215
|
+
V.length === 0 && /* @__PURE__ */ w("p", {
|
|
210
216
|
className: "rounded-md border border-dashed p-6 text-center text-sm text-muted-foreground",
|
|
211
217
|
children: [
|
|
212
218
|
"This roadmap has no items yet. Use ",
|
|
213
|
-
/* @__PURE__ */
|
|
219
|
+
/* @__PURE__ */ C("span", {
|
|
214
220
|
className: "font-medium",
|
|
215
221
|
children: "Add item"
|
|
216
222
|
}),
|
|
217
223
|
" to plan the first one."
|
|
218
224
|
]
|
|
219
225
|
}),
|
|
220
|
-
Object.entries(
|
|
226
|
+
Object.entries(G).map(([e, t]) => /* @__PURE__ */ w(E, { children: [/* @__PURE__ */ C(O, { children: /* @__PURE__ */ w(k, { children: [
|
|
221
227
|
e,
|
|
222
228
|
" (",
|
|
223
229
|
t.length,
|
|
224
230
|
")"
|
|
225
|
-
] }) }), /* @__PURE__ */
|
|
231
|
+
] }) }), /* @__PURE__ */ C(D, {
|
|
226
232
|
className: "space-y-3",
|
|
227
|
-
children: t.map((e) => /* @__PURE__ */
|
|
233
|
+
children: t.map((e) => /* @__PURE__ */ w("div", {
|
|
228
234
|
className: "flex items-center justify-between border rounded-md p-3",
|
|
229
|
-
children: [/* @__PURE__ */
|
|
230
|
-
/* @__PURE__ */
|
|
235
|
+
children: [/* @__PURE__ */ w("div", { children: [
|
|
236
|
+
/* @__PURE__ */ C("p", {
|
|
231
237
|
className: "text-sm font-medium",
|
|
232
238
|
children: e.title
|
|
233
239
|
}),
|
|
234
|
-
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */
|
|
240
|
+
e.featureRequestDemand && e.featureRequestDemand.requests > 0 && /* @__PURE__ */ w("p", {
|
|
235
241
|
className: "text-xs text-muted-foreground",
|
|
236
242
|
"data-testid": "roadmap-item-demand",
|
|
237
243
|
children: [
|
|
@@ -245,41 +251,45 @@ function M() {
|
|
|
245
251
|
e.featureRequestDemand.votes === 1 ? "" : "s"
|
|
246
252
|
]
|
|
247
253
|
}),
|
|
248
|
-
e.description && /* @__PURE__ */
|
|
254
|
+
e.description && /* @__PURE__ */ C("p", {
|
|
249
255
|
className: "text-xs text-muted-foreground",
|
|
250
256
|
children: e.description.length > 60 ? `${e.description.slice(0, 60)}…` : e.description
|
|
251
257
|
}),
|
|
252
|
-
(e.startDate || e.endDate) && /* @__PURE__ */
|
|
258
|
+
(e.startDate || e.endDate) && /* @__PURE__ */ w("div", {
|
|
253
259
|
className: "flex items-center gap-1 mt-1 text-xs text-muted-foreground",
|
|
254
260
|
children: [
|
|
255
|
-
/* @__PURE__ */
|
|
261
|
+
/* @__PURE__ */ C(_, { className: "h-3 w-3" }),
|
|
256
262
|
e.startDate ? new Date(e.startDate).toLocaleDateString() : "?",
|
|
257
263
|
" - ",
|
|
258
264
|
e.endDate ? new Date(e.endDate).toLocaleDateString() : "?"
|
|
259
265
|
]
|
|
260
266
|
})
|
|
261
|
-
] }), /* @__PURE__ */
|
|
267
|
+
] }), /* @__PURE__ */ w("div", {
|
|
262
268
|
className: "flex items-center gap-2",
|
|
263
269
|
children: [
|
|
264
|
-
e.color && /* @__PURE__ */
|
|
270
|
+
e.color && /* @__PURE__ */ C("div", {
|
|
265
271
|
className: "h-3 w-3 rounded-full",
|
|
266
272
|
style: { backgroundColor: e.color }
|
|
267
273
|
}),
|
|
268
|
-
/* @__PURE__ */
|
|
269
|
-
className: `text-xs px-2 py-0.5 rounded-full capitalize ${
|
|
274
|
+
/* @__PURE__ */ C("span", {
|
|
275
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${M[e.status] ?? "bg-gray-100"}`,
|
|
270
276
|
children: e.status.toLowerCase().replace("_", " ")
|
|
271
277
|
}),
|
|
272
|
-
/* @__PURE__ */
|
|
273
|
-
|
|
278
|
+
/* @__PURE__ */ C(o, {
|
|
279
|
+
quadrant: e.valueEffortQuadrant,
|
|
280
|
+
hideWhenUnscored: !0
|
|
281
|
+
}),
|
|
282
|
+
/* @__PURE__ */ C(d, {
|
|
283
|
+
roadmapId: B.id,
|
|
274
284
|
itemId: e.id,
|
|
275
285
|
itemTitle: e.title
|
|
276
286
|
}),
|
|
277
|
-
/* @__PURE__ */
|
|
278
|
-
roadmapId:
|
|
287
|
+
/* @__PURE__ */ C(l, {
|
|
288
|
+
roadmapId: B.id,
|
|
279
289
|
item: e,
|
|
280
|
-
lanes:
|
|
290
|
+
lanes: W
|
|
281
291
|
}),
|
|
282
|
-
/* @__PURE__ */
|
|
292
|
+
/* @__PURE__ */ C(s, {
|
|
283
293
|
id: e.id,
|
|
284
294
|
title: e.title
|
|
285
295
|
})
|
|
@@ -288,57 +298,57 @@ function M() {
|
|
|
288
298
|
}, e.id))
|
|
289
299
|
})] }, e))
|
|
290
300
|
]
|
|
291
|
-
}), /* @__PURE__ */
|
|
301
|
+
}), /* @__PURE__ */ C("div", {
|
|
292
302
|
className: "space-y-6",
|
|
293
|
-
children: /* @__PURE__ */
|
|
303
|
+
children: /* @__PURE__ */ w(E, { children: [/* @__PURE__ */ C(O, { children: /* @__PURE__ */ C(k, { children: "Details" }) }), /* @__PURE__ */ w(D, {
|
|
294
304
|
className: "space-y-2 text-sm",
|
|
295
305
|
children: [
|
|
296
|
-
/* @__PURE__ */
|
|
306
|
+
/* @__PURE__ */ w("div", {
|
|
297
307
|
className: "flex justify-between",
|
|
298
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ C("span", {
|
|
299
309
|
className: "text-muted-foreground",
|
|
300
310
|
children: "Planning focus"
|
|
301
|
-
}), /* @__PURE__ */
|
|
311
|
+
}), /* @__PURE__ */ C(i, { focus: B.planningFocus })]
|
|
302
312
|
}),
|
|
303
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ w("div", {
|
|
304
314
|
className: "flex justify-between",
|
|
305
|
-
children: [/* @__PURE__ */
|
|
315
|
+
children: [/* @__PURE__ */ C("span", {
|
|
306
316
|
className: "text-muted-foreground",
|
|
307
317
|
children: "Owner"
|
|
308
|
-
}), /* @__PURE__ */
|
|
318
|
+
}), /* @__PURE__ */ C("span", { children: /* @__PURE__ */ C(t, { id: B.ownerId }) })]
|
|
309
319
|
}),
|
|
310
|
-
/* @__PURE__ */
|
|
320
|
+
/* @__PURE__ */ w("div", {
|
|
311
321
|
className: "flex justify-between",
|
|
312
|
-
children: [/* @__PURE__ */
|
|
322
|
+
children: [/* @__PURE__ */ C("span", {
|
|
313
323
|
className: "text-muted-foreground",
|
|
314
324
|
children: "Created"
|
|
315
|
-
}), /* @__PURE__ */
|
|
325
|
+
}), /* @__PURE__ */ C("span", { children: new Date(B.createdAt).toLocaleDateString() })]
|
|
316
326
|
}),
|
|
317
|
-
/* @__PURE__ */
|
|
327
|
+
/* @__PURE__ */ w("div", {
|
|
318
328
|
className: "flex justify-between",
|
|
319
|
-
children: [/* @__PURE__ */
|
|
329
|
+
children: [/* @__PURE__ */ C("span", {
|
|
320
330
|
className: "text-muted-foreground",
|
|
321
331
|
children: "Updated"
|
|
322
|
-
}), /* @__PURE__ */
|
|
332
|
+
}), /* @__PURE__ */ C("span", { children: new Date(B.updatedAt).toLocaleDateString() })]
|
|
323
333
|
})
|
|
324
334
|
]
|
|
325
335
|
})] })
|
|
326
336
|
})]
|
|
327
337
|
}),
|
|
328
|
-
/* @__PURE__ */
|
|
338
|
+
/* @__PURE__ */ w("div", {
|
|
329
339
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
330
|
-
children: [/* @__PURE__ */
|
|
340
|
+
children: [/* @__PURE__ */ C("div", {
|
|
331
341
|
className: "lg:col-span-2",
|
|
332
|
-
children: /* @__PURE__ */
|
|
333
|
-
}), /* @__PURE__ */
|
|
342
|
+
children: /* @__PURE__ */ C(u, { roadmapId: B.id })
|
|
343
|
+
}), /* @__PURE__ */ w("div", {
|
|
334
344
|
className: "space-y-6",
|
|
335
|
-
children: [/* @__PURE__ */
|
|
345
|
+
children: [/* @__PURE__ */ C(m, { roadmapId: B.id }), /* @__PURE__ */ C(h, { roadmapId: B.id })]
|
|
336
346
|
})]
|
|
337
347
|
})
|
|
338
348
|
]
|
|
339
349
|
});
|
|
340
350
|
}
|
|
341
351
|
//#endregion
|
|
342
|
-
export {
|
|
352
|
+
export { N as ProductsRoadmapDetailPagePage };
|
|
343
353
|
|
|
344
354
|
//# 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":";;;;;;;;;;;;;;;;;;AAuBA,IAAM,IAAc,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DjB,KAAiB,MAAuC,MAAM,cAAc,MAAM,OAElF,IAAsC;CAC1C,MAAM;CACN,aAAa;CACb,MAAM;CACN,WAAW;CACX,SAAS;CACT,SAAS;CACV;AAED,SAAgB,IAAgC;CAC9C,IAAM,EAAE,mBAAgB,GAAe,EAEjC,IADS,GAA2B,CACjB,MAAM,IAIzB,CAAC,GAAM,KAAgB,QAA4B;EACvD,IAAM,IAAU,EAAa,OAAO;AACpC,SAAO,EAAc,EAAQ,GAAG,IAAU;GAC1C,EACI,KAAW,MAAsB;AAErC,EADA,EAAa,EAAK,EAClB,EAAc,QAAQ,MAAS,aAAa,OAAO,EAAK;IAGpD,EACJ,SACA,YACA,aAGA,EAA8B,GAAa;EACzC,WAAW,EAAE,IAAI,GAAW;EAC5B,MAAM,CAAC;EACR,CAAC;AAMJ,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,4BAAyB,EAAM,QAAc;;CAG5F,IAAM,IAAU,GAAM;AACtB,KAAI,CAAC,EAAS,QAAO,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAAwB,CAAA;CAExF,IAAM,KAA4B,EAAQ,SAAS,EAAE,EAClD,OAAO,CACP,MAAM,GAAoB,MAAuB,EAAE,YAAY,EAAE,UAAU,EACxE,IAAiB,EAAM,QAAQ,MAAM,EAAE,WAAW,eAAe,EAAE,WAAW,OAAO,EACrF,IAAW,EAAM,SAAS,IAAI,KAAK,MAAO,EAAe,SAAS,EAAM,SAAU,IAAI,GAAG,GAEzF,IAAQ,CAAC,GAAG,IAAI,IAAI,EAAM,KAAK,MAAM,EAAE,SAAS,CAAC,OAAO,QAAQ,CAAC,CAAC,EAGlE,IAAoD,EAAE;AAC5D,MAAK,IAAM,KAAQ,GAAO;EACxB,IAAM,IAAO,EAAK,YAAY;AAE9B,EADK,EAAe,OAAO,EAAe,KAAQ,EAAE,GACpD,EAAe,GAAM,KAAK,EAAK;;AAGjC,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAK,WAAU,wBAAyB,CAAA,EACxC,kBAAC,MAAD;MAAI,WAAU;gBAA0B,EAAQ;MAAU,CAAA,CACtD;QACN,kBAAC,KAAD;KAAG,WAAU;eACV,EAAQ,eAAe;KACtB,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CAGE,kBAAC,GAAD;OACE,aAAa,EAAQ;OACrB,oBAAoB,EAAQ;OACtB;OACC;OACP,CAAA,EACF,kBAAC,GAAD;OAAsB,WAAW,EAAQ;OAAI,MAAM,EAAQ;OAAQ,CAAA,CAC/D;SACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAO,SAAS,EAAQ,WAAW,YAAY;OAAa,WAAU;iBACnE,EAAQ,WAAW,WAAW;OACzB,CAAA,EACR,kBAAC,GAAD,EAAoB,OAAO,EAAQ,eAAiB,CAAA,CAChD;QACF;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MAAE,OAAO;MAAY,OAAO,GAAG,EAAS;MAAI;KAC5C;MAAE,OAAO;MAAe,OAAO,EAAM;MAAQ;KAC7C;MAAE,OAAO;MAAa,OAAO,EAAe;MAAQ;KACpD;MAAE,OAAO;MAAkB,OAAO,EAAmB,EAAQ,cAAc;MAAE;KAC7E;MAAE,OAAO;MAAc,OAAO,EAAQ,WAAW,WAAW;MAAW;KACxE,CAAC,KAAK,MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAiC,EAAE;MAAU,CAAA,EAC1D,kBAAC,KAAD;MAAG,WAAU;gBAAoC,EAAE;MAAU,CAAA,CACjD;QACT,EALI,EAAE,MAKN,CACP;IACE,CAAA;GAEN,kBAAC,OAAD;IACE,WAAU;IACV,MAAK;IACL,cAAW;IACX,eAAY;cAGV,CACE;KAAE,KAAK;KAAY,OAAO;KAAY,EACtC;KAAE,KAAK;KAAO,OAAO;KAAsB,CAC5C,CACD,KAAK,MACL,kBAAC,UAAD;KAEE,MAAK;KACL,gBAAc,MAAS,EAAE;KACzB,eAAe,EAAQ,EAAE,IAAI;KAC7B,WAAW,gCAAgC,MAAS,EAAE,MAAM,uCAAuC;eAElG,EAAE;KACI,EAPF,EAAE,IAOA,CACT;IACE,CAAA;GAEL,MAAS,SAAS,kBAAC,GAAD,EAAqB,WAAW,EAAQ,IAAM,CAAA;GAEhE,MAAS,cACR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;OAAY,WAAU;iBAAtB,CACE,kBAAC,GAAD,EAAA,UAAW,qBAA6B,CAAA,EACxC,kBAAC,GAAD;QAAmB,WAAW,EAAQ;QAAW;QAAS,CAAA,CAC/C;UACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAyB,CAAA,EACjE,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+B,GAAS,IAAQ;UAC5C;UACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAU;QACV,OAAO,EAAE,OAAO,GAAG,EAAS,IAAI;QAChC,CAAA;OACE,CAAA,CACM,EAAA,CAAA,CACT,EAAA,CAAA;MAEN,EAAM,WAAW,KAChB,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAA6F;QACxD,kBAAC,QAAD;SAAM,WAAU;mBAAc;SAAe,CAAA;;QAE9E;;MAEL,OAAO,QAAQ,EAAe,CAAC,KAAK,CAAC,GAAM,OAC1C,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;OACG;OAAK;OAAG,EAAU;OAAO;OAChB,EAAA,CAAA,EACD,CAAA,EACb,kBAAC,GAAD;OAAa,WAAU;iBACpB,EAAU,KAAK,MACd,kBAAC,OAAD;QAEE,WAAU;kBAFZ,CAIE,kBAAC,OAAD,EAAA,UAAA;SACE,kBAAC,KAAD;UAAG,WAAU;oBAAuB,EAAK;UAAU,CAAA;SAElD,EAAK,wBAAwB,EAAK,qBAAqB,WAAW,KACjE,kBAAC,KAAD;UACE,WAAU;UACV,eAAY;oBAFd;WAIG,EAAK,qBAAqB;WAAS;WACnC,EAAK,qBAAqB,aAAa,IAAI,KAAK;WAAI;WAAG;WACvD,EAAK,qBAAqB;WAAM;WAChC,EAAK,qBAAqB,UAAU,IAAI,KAAK;WAC5C;;SAEL,EAAK,eACJ,kBAAC,KAAD;UAAG,WAAU;oBACV,EAAK,YAAY,SAAS,KACvB,GAAG,EAAK,YAAY,MAAM,GAAG,GAAG,CAAC,KACjC,EAAK;UACP,CAAA;UAEJ,EAAK,aAAa,EAAK,YACvB,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;WAC/B,EAAK,YAAY,IAAI,KAAK,EAAK,UAAU,CAAC,oBAAoB,GAAG;WACjE;WACA,EAAK,UAAU,IAAI,KAAK,EAAK,QAAQ,CAAC,oBAAoB,GAAG;WAC1D;;SAEJ,EAAA,CAAA,EACN,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACG,EAAK,SACJ,kBAAC,OAAD;WACE,WAAU;WACV,OAAO,EAAE,iBAAiB,EAAK,OAAO;WACtC,CAAA;UAEJ,kBAAC,QAAD;WACE,WAAW,+CAA+C,EAAY,EAAK,WAAW;qBAErF,EAAK,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;WACvC,CAAA;UACP,kBAAC,GAAD;WACE,WAAW,EAAQ;WACnB,QAAQ,EAAK;WACb,WAAW,EAAK;WAChB,CAAA;UACF,kBAAC,GAAD;WAAmB,WAAW,EAAQ;WAAU;WAAa;WAAS,CAAA;UACtE,kBAAC,GAAD;WAAyB,IAAI,EAAK;WAAI,OAAO,EAAK;WAAS,CAAA;UACvD;WACF;UArDC,EAAK,GAqDN,CACN;OACU,CAAA,CACT,EAAA,EAjEI,EAiEJ,CACP;MACE;QAEN,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA,EACnB,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAqB,CAAA,EAC7D,kBAAC,GAAD,EAAoB,OAAO,EAAQ,eAAiB,CAAA,CAChD;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAY,CAAA,EACpD,kBAAC,QAAD,EAAA,UACE,kBAAC,GAAD,EAAY,IAAI,EAAQ,SAAW,CAAA,EAC9B,CAAA,CACH;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC3D;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC3D;;OACM;QACT,EAAA,CAAA;KACH,CAAA,CACF;;GAKR,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAwB,WAAW,EAAQ,IAAM,CAAA;KAC7C,CAAA,EACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAoB,WAAW,EAAQ,IAAM,CAAA,EAC7C,kBAAC,GAAD,EAAsB,WAAW,EAAQ,IAAM,CAAA,CAC3C;OACF;;GACF"}
|
|
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';\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 // 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 {/* 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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"}
|
|
@@ -26,6 +26,12 @@ export interface ProductObjectiveRef {
|
|
|
26
26
|
title: string;
|
|
27
27
|
status: string;
|
|
28
28
|
}
|
|
29
|
+
export interface ProductCompetitorRef {
|
|
30
|
+
id: string;
|
|
31
|
+
name: string;
|
|
32
|
+
description: string | null;
|
|
33
|
+
website: string | null;
|
|
34
|
+
}
|
|
29
35
|
/** Every query that lists products or shows a product's detail/links. */
|
|
30
36
|
export declare const PRODUCT_QUERIES: string[];
|
|
31
37
|
export declare function ProductHeaderActions({ productId, name, archivedAt, }: {
|
|
@@ -49,4 +55,31 @@ export declare function ProductObjectivesPanel({ productId, objectives, }: {
|
|
|
49
55
|
productId: string;
|
|
50
56
|
objectives: ProductObjectiveRef[];
|
|
51
57
|
}): import("react/jsx-runtime").JSX.Element;
|
|
58
|
+
export interface ProductAboutValues {
|
|
59
|
+
id: string;
|
|
60
|
+
mission: string | null;
|
|
61
|
+
valueProposition: string | null;
|
|
62
|
+
businessModel: string | null;
|
|
63
|
+
targetSegments: string[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* The product detail page's "About this product" section (PRM-02): mission, value proposition, business model
|
|
67
|
+
* and target segments, shown read-only with an inline "Edit" toggle for the four fields. Mirrors
|
|
68
|
+
* `FeaturePrioritizationCard`'s own stays-mounted-across-save shape (not a dialog) so a validation refusal from
|
|
69
|
+
* the server surfaces right where the fields are, and the saved values show immediately without navigating.
|
|
70
|
+
*/
|
|
71
|
+
export declare function ProductAboutPanel({ product }: {
|
|
72
|
+
product: ProductAboutValues;
|
|
73
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
74
|
+
/**
|
|
75
|
+
* The product detail page's "Competitive landscape" panel (PRM-02): the competitors linked to this product, a
|
|
76
|
+
* picker to link another from the workspace directory (`CompetitorSearchSelect`, shared with the feature
|
|
77
|
+
* request detail page's own competitor panel), and an unlink confirm per row — unlike the feature request
|
|
78
|
+
* panel's plain X, PRM-02 asks for a confirm step here since a product's competitive landscape is reviewed
|
|
79
|
+
* material, not a quick per-request note.
|
|
80
|
+
*/
|
|
81
|
+
export declare function ProductCompetitorsPanel({ productId, competitors, }: {
|
|
82
|
+
productId: string;
|
|
83
|
+
competitors: ProductCompetitorRef[];
|
|
84
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
52
85
|
//# sourceMappingURL=ProductControls.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/ProductControls.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/product/ProductControls.tsx"],"names":[],"mappings":"AA6BA;;;;;;;GAOG;AAIH,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AACD,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AACD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAED,yEAAyE;AACzE,eAAO,MAAM,eAAe,UAA2D,CAAC;AA0BxF,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,IAAI,EACJ,UAAU,GACX,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,2CAyEA;AA8BD,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,2CA+DA;AA0JD,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,iBAAiB,EAAE,CAAC;CAC/B,2CAqEA;AAkKD,wBAAgB,2BAA2B,CAAC,EAC1C,SAAS,EACT,eAAe,GAChB,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,eAAe,EAAE,wBAAwB,EAAE,CAAC;CAC7C,2CAuEA;AAyJD,wBAAgB,sBAAsB,CAAC,EACrC,SAAS,EACT,UAAU,GACX,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,mBAAmB,EAAE,CAAC;CACnC,2CAuEA;AA6ID,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B;AA4FD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,kBAAkB,CAAA;CAAE,2CA0J7E;AAiBD;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,SAAS,EACT,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC,2CA4FA"}
|