@burdenoff/microfe-planmagnet 2026.928.9 → 2026.928.10

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.
Files changed (30) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +217 -209
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
  8. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +198 -99
  9. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
  10. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  11. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +260 -170
  12. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  13. package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.d.ts +2 -0
  14. package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.d.ts.map +1 -0
  15. package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.js +283 -0
  16. package/dist/planmagnet/pages/ProductsEditRoadmapPagePage.js.map +1 -0
  17. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  18. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +166 -246
  19. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  20. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  21. package/dist/planmagnet/pages/ProductsProductsPagePage.js +147 -117
  22. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  23. package/dist/planmagnet/product/ProductControls.d.ts +52 -0
  24. package/dist/planmagnet/product/ProductControls.d.ts.map +1 -0
  25. package/dist/planmagnet/product/ProductControls.js +780 -0
  26. package/dist/planmagnet/product/ProductControls.js.map +1 -0
  27. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  28. package/dist/planmagnet/roadmaps/RoadmapControls.js +1 -1
  29. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  30. package/package.json +1 -1
@@ -1,286 +1,206 @@
1
- import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as t } from "../people/People.js";
3
- import { PlanningFocusBadge as n } from "../roadmaps/planningFocus.js";
4
- import { RoadmapHeaderActions as r } from "../roadmaps/RoadmapControls.js";
5
- import { Calendar as i, GitBranch as a, Loader2 as o, Map as s, Star as c, Users as l } from "lucide-react";
6
- import { useParams as u } from "react-router";
7
- import { gql as d } from "@apollo/client";
8
- import { useQuery as f } from "@apollo/client/react";
9
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
10
- import { Badge as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y } from "@burdenoff/fe-libs/ui";
3
+ import { useEntityId as n } from "../hooks/useEntityId.js";
4
+ import { enumLabel as r } from "../controls/WorkItemControls.js";
5
+ import { detailPath as i } from "../utils/rowNavigation.js";
6
+ import { PlanMagnetNotFoundPage as a } from "./NotFoundPage.js";
7
+ import { ProductFeatureRequestsPanel as o, ProductHeaderActions as s, ProductObjectivesPanel as c, ProductReleasesPanel as l, ProductRoadmapsPanel as u } from "../product/ProductControls.js";
8
+ import { Calendar as d, Loader2 as f, Package as p, Pencil as m, Target as h, Users as g } from "lucide-react";
9
+ import { gql as _ } from "@apollo/client";
10
+ import { useQuery as v } from "@apollo/client/react";
11
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
12
+ import { Badge as x, Button as S, Card as C, CardContent as w, CardHeader as T, CardTitle as E } from "@burdenoff/fe-libs/ui";
11
13
  //#region src/planmagnet/pages/ProductsProductDetailPagePage.tsx
12
- var b = d`
13
- query GetRoadmapDetail($id: ID!) {
14
- planmagnetRoadmap(id: $id) {
14
+ var D = _`
15
+ query GetProductDetail($id: ID!) {
16
+ planmagnetProduct(id: $id) {
15
17
  id
16
18
  workspaceId
17
19
  name
18
20
  description
19
- type
20
- planningFocus
21
- isPublic
21
+ vision
22
+ lifecycleStage
22
23
  ownerId
23
- createdAt
24
- updatedAt
25
- items {
24
+ archivedAt
25
+ northStar {
26
+ name
27
+ unit
28
+ target
29
+ current
30
+ window
31
+ measuredAt
32
+ progressRatio
33
+ }
34
+ roadmaps {
26
35
  id
27
- title
28
- description
36
+ name
37
+ }
38
+ releases {
39
+ id
40
+ name
41
+ version
29
42
  status
30
- startDate
31
- endDate
32
- color
33
- swimlane
34
- sortOrder
35
43
  }
36
- }
37
- }
38
- `, x = d`
39
- query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {
40
- planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {
41
- items {
44
+ featureRequests {
42
45
  id
43
46
  title
44
- description
45
47
  status
46
- priorityScore
47
- voteCount
48
- businessValue
49
- createdAt
50
48
  }
51
- totalCount
52
- }
53
- }
54
- `, S = d`
55
- query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {
56
- planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {
57
- items {
49
+ objectives {
58
50
  id
59
- name
60
- version
51
+ title
61
52
  status
62
- plannedDate
63
53
  }
64
- totalCount
54
+ createdAt
55
+ updatedAt
65
56
  }
66
57
  }
67
58
  `;
68
- function C() {
69
- let { workspaceId: d } = e(), C = u().id ?? "", { data: w, loading: T, error: E } = f(b, {
70
- variables: { id: C },
71
- skip: !C
72
- }), { data: D } = f(x, {
73
- variables: {
74
- workspaceId: d,
75
- pagination: { first: 20 }
76
- },
77
- skip: !d
78
- }), { data: O } = f(S, {
79
- variables: {
80
- workspaceId: d,
81
- pagination: { first: 20 }
82
- },
83
- skip: !d
59
+ function O() {
60
+ let _ = e(), O = n("productId") ?? "", { data: k, loading: A, error: j } = v(D, {
61
+ variables: { id: O },
62
+ skip: !O
63
+ });
64
+ if (!O) return /* @__PURE__ */ y("div", {
65
+ className: "p-6 text-muted-foreground",
66
+ children: "No product selected. Open one from the list."
84
67
  });
85
- if (T && !w) return /* @__PURE__ */ p("div", {
68
+ if (A && !k) return /* @__PURE__ */ y("div", {
86
69
  className: "flex items-center justify-center p-12",
87
- children: /* @__PURE__ */ p(o, { className: "h-8 w-8 animate-spin text-muted-foreground" })
70
+ children: /* @__PURE__ */ y(f, { className: "h-8 w-8 animate-spin text-muted-foreground" })
88
71
  });
89
- if (E) return /* @__PURE__ */ m("div", {
72
+ if (j) return /* @__PURE__ */ b("div", {
90
73
  className: "p-6 text-destructive",
91
- children: ["Failed to load product: ", E.message]
74
+ children: ["Failed to load product: ", j.message]
92
75
  });
93
- let k = w?.planmagnetRoadmap;
94
- if (!k) return /* @__PURE__ */ p("div", {
95
- className: "p-6 text-muted-foreground",
96
- children: "Product not found."
97
- });
98
- let A = k.items ?? [], j = D?.planmagnetFeatureRequests?.items ?? [], M = O?.planmagnetReleases?.items ?? [], N = D?.planmagnetFeatureRequests?.totalCount ?? 0, P = O?.planmagnetReleases?.totalCount ?? 0;
99
- return /* @__PURE__ */ m("div", {
76
+ let M = k?.planmagnetProduct;
77
+ return M ? /* @__PURE__ */ b("div", {
100
78
  className: "space-y-6 p-6",
101
79
  children: [
102
- /* @__PURE__ */ m("div", {
103
- className: "flex items-center justify-between",
104
- children: [/* @__PURE__ */ p("div", {
105
- className: "flex items-center gap-3",
106
- children: /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
107
- className: "text-2xl font-semibold",
108
- children: k.name
109
- }), /* @__PURE__ */ p("p", {
110
- className: "text-sm text-muted-foreground",
111
- children: k.description ?? "No description"
112
- })] })
113
- }), /* @__PURE__ */ m("div", {
80
+ /* @__PURE__ */ b("div", {
81
+ className: "flex items-start justify-between gap-4",
82
+ children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ b("div", {
83
+ className: "flex items-center gap-2",
84
+ children: [
85
+ /* @__PURE__ */ y(p, { className: "h-5 w-5 text-primary" }),
86
+ /* @__PURE__ */ y("h1", {
87
+ className: "text-2xl font-semibold",
88
+ children: M.name
89
+ }),
90
+ M.archivedAt && /* @__PURE__ */ y(x, {
91
+ variant: "outline",
92
+ children: "Archived"
93
+ }),
94
+ /* @__PURE__ */ y(x, {
95
+ variant: "secondary",
96
+ className: "capitalize",
97
+ children: r(M.lifecycleStage)
98
+ })
99
+ ]
100
+ }), /* @__PURE__ */ y("p", {
101
+ className: "text-sm text-muted-foreground mt-1",
102
+ children: M.vision || "No vision set"
103
+ })] }), /* @__PURE__ */ y("div", {
114
104
  className: "flex flex-col items-end gap-2",
115
- children: [/* @__PURE__ */ p(r, {
116
- roadmapId: k.id,
117
- name: k.name
118
- }), /* @__PURE__ */ p(n, { focus: k.planningFocus })]
105
+ children: /* @__PURE__ */ b("div", {
106
+ className: "flex gap-2",
107
+ children: [/* @__PURE__ */ b(S, {
108
+ size: "sm",
109
+ variant: "outline",
110
+ onClick: () => _(i("/products/edit-product-page", M.id)),
111
+ children: [/* @__PURE__ */ y(m, { className: "mr-1 h-4 w-4" }), "Edit"]
112
+ }), /* @__PURE__ */ y(s, {
113
+ productId: M.id,
114
+ name: M.name,
115
+ archivedAt: M.archivedAt
116
+ })]
117
+ })
119
118
  })]
120
119
  }),
121
- /* @__PURE__ */ p("div", {
120
+ /* @__PURE__ */ b("div", {
122
121
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
123
122
  children: [
124
- {
125
- label: "Roadmap Items",
126
- value: A.length,
127
- icon: c
128
- },
129
- {
130
- label: "Releases in workspace",
131
- value: P,
132
- icon: a
133
- },
134
- {
135
- label: "Feature requests in workspace",
136
- value: N,
137
- icon: s
138
- },
139
- {
140
- label: "Owner",
141
- value: /* @__PURE__ */ p(t, { id: k.ownerId }),
142
- icon: l
143
- }
144
- ].map((e) => /* @__PURE__ */ p(g, { children: /* @__PURE__ */ m(_, {
145
- className: "pt-4",
146
- children: [/* @__PURE__ */ m("div", {
147
- className: "flex items-center gap-2 text-muted-foreground text-sm",
148
- children: [/* @__PURE__ */ p(e.icon, { className: "h-4 w-4" }), e.label]
149
- }), /* @__PURE__ */ p("p", {
150
- className: "text-xl font-semibold mt-1",
151
- children: e.value
152
- })]
153
- }) }, e.label))
154
- }),
155
- /* @__PURE__ */ m("div", {
156
- className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
157
- children: [/* @__PURE__ */ m("div", {
158
- className: "lg:col-span-2 space-y-6",
159
- children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, { children: ["Latest feature requests in the workspace", N > j.length ? ` (${j.length} of ${N})` : ""] }) }), /* @__PURE__ */ m(_, {
160
- className: "space-y-3",
161
- children: [j.length === 0 && /* @__PURE__ */ p("p", {
162
- className: "text-sm text-muted-foreground",
163
- children: "No feature requests yet."
164
- }), j.map((e) => /* @__PURE__ */ m("div", {
165
- className: "flex items-center justify-between rounded-md border p-3",
166
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
167
- className: "text-sm font-medium",
168
- children: e.title
169
- }), /* @__PURE__ */ m("p", {
170
- className: "text-xs text-muted-foreground",
171
- children: [
172
- "Votes: ",
173
- e.voteCount,
174
- " · Business Value: ",
175
- e.businessValue ?? "N/A"
176
- ]
177
- })] }), /* @__PURE__ */ p("div", {
178
- className: "flex items-center gap-2",
179
- children: /* @__PURE__ */ p(h, {
180
- variant: "secondary",
181
- className: "capitalize text-xs",
182
- children: e.status.toLowerCase().replace("_", " ")
183
- })
184
- })]
185
- }, e.id))]
186
- })] }), /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, { children: [
187
- "Roadmap Items (",
188
- A.length,
189
- ")"
190
- ] }) }), /* @__PURE__ */ m(_, {
191
- className: "space-y-3",
192
- children: [A.length === 0 && /* @__PURE__ */ p("p", {
193
- className: "text-sm text-muted-foreground",
194
- children: "No roadmap items linked."
195
- }), A.map((e) => /* @__PURE__ */ m("div", {
196
- className: "flex items-center justify-between rounded-md border p-3",
197
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("p", {
198
- className: "text-sm font-medium",
199
- children: e.title
200
- }), /* @__PURE__ */ m("p", {
201
- className: "text-xs text-muted-foreground",
202
- children: [
203
- e.startDate ? new Date(e.startDate).toLocaleDateString() : "No start date",
204
- " - ",
205
- e.endDate ? new Date(e.endDate).toLocaleDateString() : "No end date"
206
- ]
207
- })] }), /* @__PURE__ */ p(h, {
208
- variant: "outline",
209
- className: "capitalize text-xs",
210
- children: e.status.toLowerCase().replace("_", " ")
211
- })]
212
- }, e.id))]
213
- })] })]
214
- }), /* @__PURE__ */ m("div", {
215
- className: "space-y-6",
216
- children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Latest releases in the workspace" }) }), /* @__PURE__ */ m(_, {
217
- className: "space-y-3",
218
- children: [M.length === 0 && /* @__PURE__ */ p("p", {
219
- className: "text-sm text-muted-foreground",
220
- children: "No releases yet."
221
- }), M.map((e) => /* @__PURE__ */ m("div", {
222
- className: "border-b pb-2 last:border-0 last:pb-0",
123
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
124
+ className: "pt-4",
125
+ children: [/* @__PURE__ */ b("div", {
126
+ className: "flex items-center gap-2 text-muted-foreground text-xs",
127
+ children: [/* @__PURE__ */ y(h, { className: "h-4 w-4" }), "North Star"]
128
+ }), M.northStar ? /* @__PURE__ */ b("p", {
129
+ className: "text-sm font-bold mt-1 truncate",
223
130
  children: [
224
- /* @__PURE__ */ m("div", {
225
- className: "flex items-center justify-between",
226
- children: [/* @__PURE__ */ p("span", {
227
- className: "text-sm font-medium",
228
- children: e.version
229
- }), /* @__PURE__ */ p(h, {
230
- variant: "outline",
231
- className: "text-xs capitalize",
232
- children: e.status.toLowerCase().replace("_", " ")
233
- })]
234
- }),
235
- /* @__PURE__ */ p("p", {
236
- className: "text-xs text-muted-foreground",
237
- children: e.name
238
- }),
239
- /* @__PURE__ */ m("div", {
240
- className: "flex items-center gap-1 mt-1 text-xs text-muted-foreground",
241
- children: [/* @__PURE__ */ p(i, { className: "h-3 w-3" }), e.plannedDate ? new Date(e.plannedDate).toLocaleDateString() : "No date set"]
242
- })
131
+ M.northStar.current,
132
+ " / ",
133
+ M.northStar.target,
134
+ " ",
135
+ M.northStar.unit,
136
+ M.northStar.progressRatio == null ? "" : ` (${Math.round(M.northStar.progressRatio * 100)}%)`
243
137
  ]
244
- }, e.id))]
245
- })] }), /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Details" }) }), /* @__PURE__ */ m(_, {
246
- className: "space-y-2 text-sm",
247
- children: [
248
- /* @__PURE__ */ m("div", {
249
- className: "flex justify-between",
250
- children: [/* @__PURE__ */ p("span", {
251
- className: "text-muted-foreground",
252
- children: "Created"
253
- }), /* @__PURE__ */ p("span", { children: new Date(k.createdAt).toLocaleDateString() })]
254
- }),
255
- /* @__PURE__ */ m("div", {
256
- className: "flex justify-between",
257
- children: [/* @__PURE__ */ p("span", {
258
- className: "text-muted-foreground",
259
- children: "Updated"
260
- }), /* @__PURE__ */ p("span", { children: new Date(k.updatedAt).toLocaleDateString() })]
261
- }),
262
- /* @__PURE__ */ m("div", {
263
- className: "flex justify-between",
264
- children: [/* @__PURE__ */ p("span", {
265
- className: "text-muted-foreground",
266
- children: "Owner"
267
- }), /* @__PURE__ */ p("span", { children: /* @__PURE__ */ p(t, { id: k.ownerId }) })]
268
- }),
269
- /* @__PURE__ */ m("div", {
270
- className: "flex justify-between",
271
- children: [/* @__PURE__ */ p("span", {
272
- className: "text-muted-foreground",
273
- children: "Visibility"
274
- }), /* @__PURE__ */ p("span", { children: k.isPublic ? "Public" : "Private" })]
275
- })
276
- ]
277
- })] })]
278
- })]
138
+ }) : /* @__PURE__ */ y("p", {
139
+ className: "text-sm font-medium mt-1 text-muted-foreground",
140
+ children: "Not set"
141
+ })]
142
+ }) }),
143
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
144
+ className: "pt-4",
145
+ children: [/* @__PURE__ */ b("div", {
146
+ className: "flex items-center gap-2 text-muted-foreground text-xs",
147
+ children: [/* @__PURE__ */ y(g, { className: "h-4 w-4" }), "Owner"]
148
+ }), /* @__PURE__ */ y("p", {
149
+ className: "text-sm font-bold mt-1 truncate",
150
+ children: /* @__PURE__ */ y(t, { id: M.ownerId })
151
+ })]
152
+ }) }),
153
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
154
+ className: "pt-4",
155
+ children: [/* @__PURE__ */ b("div", {
156
+ className: "flex items-center gap-2 text-muted-foreground text-xs",
157
+ children: [/* @__PURE__ */ y(d, { className: "h-4 w-4" }), "Created"]
158
+ }), /* @__PURE__ */ y("p", {
159
+ className: "text-xl font-bold mt-1",
160
+ children: new Date(M.createdAt).toLocaleDateString()
161
+ })]
162
+ }) }),
163
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
164
+ className: "pt-4",
165
+ children: [/* @__PURE__ */ b("div", {
166
+ className: "flex items-center gap-2 text-muted-foreground text-xs",
167
+ children: [/* @__PURE__ */ y(d, { className: "h-4 w-4" }), "Updated"]
168
+ }), /* @__PURE__ */ y("p", {
169
+ className: "text-xl font-bold mt-1",
170
+ children: new Date(M.updatedAt).toLocaleDateString()
171
+ })]
172
+ }) })
173
+ ]
174
+ }),
175
+ M.description && /* @__PURE__ */ y(C, { children: /* @__PURE__ */ y(w, {
176
+ className: "pt-4 text-sm text-muted-foreground",
177
+ children: M.description
178
+ }) }),
179
+ /* @__PURE__ */ b("div", {
180
+ className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
181
+ children: [
182
+ /* @__PURE__ */ b(C, { children: [/* @__PURE__ */ y(T, { children: /* @__PURE__ */ y(E, { children: "Roadmaps" }) }), /* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(u, {
183
+ productId: M.id,
184
+ roadmaps: M.roadmaps
185
+ }) })] }),
186
+ /* @__PURE__ */ b(C, { children: [/* @__PURE__ */ y(T, { children: /* @__PURE__ */ y(E, { children: "Releases" }) }), /* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(l, {
187
+ productId: M.id,
188
+ releases: M.releases
189
+ }) })] }),
190
+ /* @__PURE__ */ b(C, { children: [/* @__PURE__ */ y(T, { children: /* @__PURE__ */ y(E, { children: "Feature Requests" }) }), /* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(o, {
191
+ productId: M.id,
192
+ featureRequests: M.featureRequests
193
+ }) })] }),
194
+ /* @__PURE__ */ b(C, { children: [/* @__PURE__ */ y(T, { children: /* @__PURE__ */ y(E, { children: "OKR Objectives" }) }), /* @__PURE__ */ y(w, { children: /* @__PURE__ */ y(c, {
195
+ productId: M.id,
196
+ objectives: M.objectives
197
+ }) })] })
198
+ ]
279
199
  })
280
200
  ]
281
- });
201
+ }) : /* @__PURE__ */ y(a, {});
282
202
  }
283
203
  //#endregion
284
- export { C as ProductsProductDetailPagePage };
204
+ export { O as ProductsProductDetailPagePage };
285
205
 
286
206
  //# sourceMappingURL=ProductsProductDetailPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Package, GitBranch, Map, Star, Users, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport { PlanningFocusBadge } from '../roadmaps/planningFocus';\nimport { RoadmapHeaderActions } from '../roadmaps/RoadmapControls';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetail($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 }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_PRODUCT = gql`\n query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n title\n description\n status\n priorityScore\n voteCount\n businessValue\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES_FOR_PRODUCT = gql`\n query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n version\n status\n plannedDate\n }\n totalCount\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}\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n priorityScore: number | null;\n voteCount: number;\n businessValue: number | null;\n}\n\ninterface ReleaseNode {\n id: string;\n name: string;\n version: string;\n status: string;\n plannedDate: string | null;\n}\n\nexport function ProductsProductDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\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 const {\n data: featuresData,\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>>(LIST_FEATURE_REQUESTS_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\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>>(LIST_RELEASES_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\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 product: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Product not found.</div>;\n\n const roadmapItems: RoadmapItemNode[] = roadmap.items ?? [];\n const features: FeatureRequestNode[] = featuresData?.planmagnetFeatureRequests?.items ?? [];\n const releases: ReleaseNode[] = releasesData?.planmagnetReleases?.items ?? [];\n const totalFeatures = featuresData?.planmagnetFeatureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.planmagnetReleases?.totalCount ?? 0;\n\n const statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n </div>\n {/* Edit and Delete (BOFF-7941): the Edit page existed and nothing linked to it. */}\n <div className=\"flex flex-col items-end gap-2\">\n <RoadmapHeaderActions roadmapId={roadmap.id} name={roadmap.name} />\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n { label: 'Roadmap Items', value: roadmapItems.length, icon: Star },\n // A product (a roadmap) has no project scope of its own, so these are the WORKSPACE's numbers — said so,\n // rather than shown as if they belonged to the product (BOFF-7991).\n { label: 'Releases in workspace', value: totalReleases, icon: GitBranch },\n { label: 'Feature requests in workspace', value: totalFeatures, icon: Map },\n // The name, as the Details card below shows it — not the owner's UUID in large type (BOFF-7976).\n { label: 'Owner', value: <PersonName id={roadmap.ownerId} />, icon: Users },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\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>\n <CardTitle>\n Latest feature requests in the workspace\n {totalFeatures > features.length ? ` (${features.length} of ${totalFeatures})` : ''}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {features.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No feature requests yet.</p>\n )}\n {features.map((f) => (\n <div key={f.id} className=\"flex items-center justify-between rounded-md border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{f.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Votes: {f.voteCount} &middot; Business Value: {f.businessValue ?? 'N/A'}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"capitalize text-xs\">\n {f.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Roadmap Items ({roadmapItems.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {roadmapItems.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No roadmap items linked.</p>\n )}\n {roadmapItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {item.startDate\n ? new Date(item.startDate).toLocaleDateString()\n : 'No start date'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'No end date'}\n </p>\n </div>\n <Badge variant=\"outline\" className=\"capitalize text-xs\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Latest releases in the workspace</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {releases.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No releases yet.</p>\n )}\n {releases.map((r) => (\n <div key={r.id} className=\"border-b pb-2 last:border-0 last:pb-0\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{r.version}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {r.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{r.name}</p>\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {r.plannedDate ? new Date(r.plannedDate).toLocaleDateString() : 'No date set'}\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\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\">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 <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\">Visibility</span>\n <span>{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Package, Target, Users, Calendar, Loader2, Pencil } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { PlanMagnetNotFoundPage } from './NotFoundPage';\nimport {\n ProductHeaderActions,\n ProductRoadmapsPanel,\n ProductReleasesPanel,\n ProductFeatureRequestsPanel,\n ProductObjectivesPanel,\n type ProductRoadmapRef,\n type ProductReleaseRef,\n type ProductFeatureRequestRef,\n type ProductObjectiveRef,\n} from '../product/ProductControls';\n\n/**\n * Product detail (BOFF-8045): the product's own fields, its North Star, and a Links\n * management panel for roadmaps/releases/feature requests/OKR objectives. Every relation\n * is selected directly off `planmagnetProduct(id)` rather than a separate workspace-wide\n * query — before this rewrite the page queried EVERY feature request/release in the\n * workspace regardless of product, which is the pre-existing bug this fixes (there was no\n * productId to filter on until now).\n */\n\nconst GET_PRODUCT_DETAIL = gql`\n query GetProductDetail($id: ID!) {\n planmagnetProduct(id: $id) {\n id\n workspaceId\n name\n description\n vision\n lifecycleStage\n ownerId\n archivedAt\n northStar {\n name\n unit\n target\n current\n window\n measuredAt\n progressRatio\n }\n roadmaps {\n id\n name\n }\n releases {\n id\n name\n version\n status\n }\n featureRequests {\n id\n title\n status\n }\n objectives {\n id\n title\n status\n }\n createdAt\n updatedAt\n }\n }\n`;\n\ninterface ProductDetail {\n id: string;\n workspaceId: string;\n name: string;\n description: string | null;\n vision: string;\n lifecycleStage: string;\n ownerId: string;\n archivedAt: string | null;\n northStar: {\n name: string;\n unit: string;\n target: number;\n current: number;\n window: string;\n measuredAt: string | null;\n progressRatio: number | null;\n } | null;\n roadmaps: ProductRoadmapRef[];\n releases: ProductReleaseRef[];\n featureRequests: ProductFeatureRequestRef[];\n objectives: ProductObjectiveRef[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport function ProductsProductDetailPagePage() {\n const navigate = usePlanMagnetNavigate();\n const productId = useEntityId('productId') ?? '';\n\n const { data, loading, error } = useQuery<{ planmagnetProduct: ProductDetail | null }>(\n GET_PRODUCT_DETAIL,\n { variables: { id: productId }, skip: !productId }\n );\n\n if (!productId) {\n return (\n <div className=\"p-6 text-muted-foreground\">No product selected. Open one from the list.</div>\n );\n }\n\n // Only before the first load: a refetch must not blank the page (it\n // unmounts every component on it and loses their state).\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 product: {error.message}</div>;\n }\n\n const product = data?.planmagnetProduct;\n // Deleted, unauthorized or an unknown id all read back the same way (no cross-tenant\n // existence leak) — the real 404 page, not an inline message.\n if (!product) return <PlanMagnetNotFoundPage />;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-start justify-between gap-4\">\n <div>\n <div className=\"flex items-center gap-2\">\n <Package className=\"h-5 w-5 text-primary\" />\n <h1 className=\"text-2xl font-semibold\">{product.name}</h1>\n {product.archivedAt && <Badge variant=\"outline\">Archived</Badge>}\n <Badge variant=\"secondary\" className=\"capitalize\">\n {enumLabel(product.lifecycleStage)}\n </Badge>\n </div>\n <p className=\"text-sm text-muted-foreground mt-1\">{product.vision || 'No vision set'}</p>\n </div>\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/products/edit-product-page', product.id))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <ProductHeaderActions\n productId={product.id}\n name={product.name}\n archivedAt={product.archivedAt}\n />\n </div>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Target className=\"h-4 w-4\" />\n North Star\n </div>\n {product.northStar ? (\n <p className=\"text-sm font-bold mt-1 truncate\">\n {product.northStar.current} / {product.northStar.target} {product.northStar.unit}\n {product.northStar.progressRatio != null\n ? ` (${Math.round(product.northStar.progressRatio * 100)}%)`\n : ''}\n </p>\n ) : (\n <p className=\"text-sm font-medium mt-1 text-muted-foreground\">Not set</p>\n )}\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Users className=\"h-4 w-4\" />\n Owner\n </div>\n <p className=\"text-sm font-bold mt-1 truncate\">\n <PersonName id={product.ownerId} />\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Calendar className=\"h-4 w-4\" />\n Created\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.createdAt).toLocaleDateString()}\n </p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-xs\">\n <Calendar className=\"h-4 w-4\" />\n Updated\n </div>\n <p className=\"text-xl font-bold mt-1\">\n {new Date(product.updatedAt).toLocaleDateString()}\n </p>\n </CardContent>\n </Card>\n </div>\n\n {product.description && (\n <Card>\n <CardContent className=\"pt-4 text-sm text-muted-foreground\">\n {product.description}\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Roadmaps</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductRoadmapsPanel productId={product.id} roadmaps={product.roadmaps} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Releases</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductReleasesPanel productId={product.id} releases={product.releases} />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Feature Requests</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductFeatureRequestsPanel\n productId={product.id}\n featureRequests={product.featureRequests}\n />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>OKR Objectives</CardTitle>\n </CardHeader>\n <CardContent>\n <ProductObjectivesPanel productId={product.id} objectives={product.objectives} />\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,wBAAwB,4CA+KvC"}
1
+ {"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AA+EA,wBAAgB,wBAAwB,4CAgMvC"}