@burdenoff/microfe-planmagnet 2026.1001.7 → 2026.1001.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,17 @@
1
- import { useFeatureCounts as e } from "../hooks/useCounts.js";
2
- import { planningFocusLabel as t } from "../roadmaps/planningFocus.js";
3
- import { AlertTriangle as n, BarChart3 as r, CheckCircle as i, Loader2 as a, Target as o, TrendingUp as s } from "lucide-react";
4
- import { useParams as c } from "react-router";
5
- import { gql as l } from "@apollo/client";
6
- import { useQuery as u } from "@apollo/client/react";
7
- import { jsx as d, jsxs as f } from "react/jsx-runtime";
8
- import { Card as p, CardContent as m, CardHeader as h, CardTitle as g } from "@burdenoff/fe-libs/ui";
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { useFeatureCounts as t } from "../hooks/useCounts.js";
3
+ import { useLiveRefresh as n } from "../hooks/useLiveRefresh.js";
4
+ import { LiveStatus as r } from "../controls/LiveStatus.js";
5
+ import { planningFocusLabel as i } from "../roadmaps/planningFocus.js";
6
+ import { useState as a } from "react";
7
+ import { AlertTriangle as o, BarChart3 as s, CheckCircle as c, Loader2 as l, Target as u, TrendingUp as d } from "lucide-react";
8
+ import { useParams as f } from "react-router";
9
+ import { gql as p } from "@apollo/client";
10
+ import { useQuery as m } from "@apollo/client/react";
11
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
12
+ import { Button as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x } from "@burdenoff/fe-libs/ui";
9
13
  //#region src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.tsx
10
- var _ = l`
14
+ var S = ["GetRoadmapAnalytics"], C = p`
11
15
  query GetRoadmapAnalytics($id: ID!) {
12
16
  planmagnetRoadmap(id: $id) {
13
17
  id
@@ -30,130 +34,162 @@ var _ = l`
30
34
  }
31
35
  }
32
36
  `;
33
- function v() {
34
- let l = c().id ?? "", { data: v, loading: y, error: b } = u(_, {
35
- variables: { id: l },
36
- skip: !l
37
- }), x = e();
38
- if (y && !v) return /* @__PURE__ */ d("div", {
37
+ function w() {
38
+ let p = f().id ?? "", w = e(), { data: T, loading: E, error: D } = m(C, {
39
+ variables: { id: p },
40
+ skip: !p
41
+ }), O = t(), [k, A] = a(!1), j = n({
42
+ kind: "roadmap",
43
+ roadmapId: p,
44
+ queries: S,
45
+ onEvent: (e) => {
46
+ e.kind === "DELETED" && e.id === p && A(!0);
47
+ }
48
+ });
49
+ if (k) return /* @__PURE__ */ g("div", {
50
+ className: "space-y-3 p-6",
51
+ "data-testid": "roadmap-deleted",
52
+ children: [/* @__PURE__ */ h("p", {
53
+ className: "text-sm text-muted-foreground",
54
+ children: "This roadmap was deleted."
55
+ }), /* @__PURE__ */ h(_, {
56
+ size: "sm",
57
+ variant: "ghost",
58
+ onClick: () => w("/products/roadmaps-page"),
59
+ children: "Back to roadmaps"
60
+ })]
61
+ });
62
+ if (E && !T) return /* @__PURE__ */ h("div", {
39
63
  className: "flex items-center justify-center p-12",
40
- children: /* @__PURE__ */ d(a, { className: "h-8 w-8 animate-spin text-muted-foreground" })
64
+ children: /* @__PURE__ */ h(l, { className: "h-8 w-8 animate-spin text-muted-foreground" })
41
65
  });
42
- if (b) return /* @__PURE__ */ f("div", {
66
+ if (D) return /* @__PURE__ */ g("div", {
43
67
  className: "p-6 text-destructive",
44
- children: ["Failed to load analytics: ", b.message]
68
+ children: ["Failed to load analytics: ", D.message]
45
69
  });
46
- let S = v?.planmagnetRoadmap;
47
- if (!S) return /* @__PURE__ */ d("div", {
48
- className: "p-6 text-muted-foreground",
49
- children: "Roadmap not found."
70
+ let M = T?.planmagnetRoadmap;
71
+ if (!M) return /* @__PURE__ */ g("div", {
72
+ className: "space-y-3 p-6",
73
+ "data-testid": "roadmap-not-found",
74
+ children: [/* @__PURE__ */ h("p", {
75
+ className: "text-sm text-muted-foreground",
76
+ children: "This roadmap does not exist in this workspace, or it was deleted."
77
+ }), /* @__PURE__ */ h(_, {
78
+ size: "sm",
79
+ variant: "ghost",
80
+ onClick: () => w("/products/roadmaps-page"),
81
+ children: "Back to roadmaps"
82
+ })]
50
83
  });
51
- let C = S.items ?? [], w = (e) => e.status === "COMPLETED" || e.status === "DONE", T = C.filter((e) => e.status !== "CANCELLED"), E = T.filter(w), D = T.length > 0 ? Math.round(E.length / T.length * 100) : 0, O = {};
52
- for (let e of C) O[e.status] = (O[e.status] || 0) + 1;
53
- let k = x ? Object.fromEntries(Object.entries(x.byStatus).filter(([, e]) => e > 0)) : {}, A = {};
54
- for (let e of C) {
84
+ let N = M.items ?? [], P = (e) => e.status === "COMPLETED" || e.status === "DONE", F = N.filter((e) => e.status !== "CANCELLED"), I = F.filter(P), L = F.length > 0 ? Math.round(I.length / F.length * 100) : 0, R = {};
85
+ for (let e of N) R[e.status] = (R[e.status] || 0) + 1;
86
+ let z = O ? Object.fromEntries(Object.entries(O.byStatus).filter(([, e]) => e > 0)) : {}, B = {};
87
+ for (let e of N) {
55
88
  let t = e.swimlane ?? "Uncategorized";
56
- A[t] = (A[t] || 0) + 1;
89
+ B[t] = (B[t] || 0) + 1;
57
90
  }
58
- let j = /* @__PURE__ */ new Date(), M = T.filter((e) => e.endDate && new Date(e.endDate) < j && !w(e)), N = T.filter((e) => !w(e)), P = N.length > 0 ? Math.round(N.filter((e) => !M.includes(e) && e.status !== "AT_RISK" && e.status !== "DELAYED").length / N.length * 100) : null, F = {};
59
- for (let e of T) if (e.endDate) {
91
+ let V = /* @__PURE__ */ new Date(), H = F.filter((e) => e.endDate && new Date(e.endDate) < V && !P(e)), U = F.filter((e) => !P(e)), W = U.length > 0 ? Math.round(U.filter((e) => !H.includes(e) && e.status !== "AT_RISK" && e.status !== "DELAYED").length / U.length * 100) : null, G = {};
92
+ for (let e of F) if (e.endDate) {
60
93
  let t = e.endDate.slice(0, 7);
61
- F[t] || (F[t] = {
94
+ G[t] || (G[t] = {
62
95
  planned: 0,
63
96
  completed: 0
64
- }), F[t].planned += 1, w(e) && (F[t].completed += 1);
97
+ }), G[t].planned += 1, P(e) && (G[t].completed += 1);
65
98
  }
66
- let I = Object.entries(F).sort(([e], [t]) => e.localeCompare(t)).slice(-6).map(([e, t]) => ({
99
+ let K = Object.entries(G).sort(([e], [t]) => e.localeCompare(t)).slice(-6).map(([e, t]) => ({
67
100
  month: (/* @__PURE__ */ new Date(`${e}-01T00:00:00Z`)).toLocaleDateString("en-US", {
68
101
  month: "short",
69
102
  year: "2-digit",
70
103
  timeZone: "UTC"
71
104
  }),
72
105
  ...t
73
- })), L = Math.max(...I.map((e) => Math.max(e.planned, e.completed)), 1);
74
- return /* @__PURE__ */ f("div", {
106
+ })), q = Math.max(...K.map((e) => Math.max(e.planned, e.completed)), 1);
107
+ return /* @__PURE__ */ g("div", {
75
108
  className: "space-y-6 p-6",
76
109
  children: [
77
- /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
78
- className: "text-2xl font-semibold",
79
- children: "Roadmap Analytics"
80
- }), /* @__PURE__ */ f("p", {
110
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ g("div", {
111
+ className: "flex items-center gap-2",
112
+ children: [/* @__PURE__ */ h("h1", {
113
+ className: "text-2xl font-semibold",
114
+ children: "Roadmap Analytics"
115
+ }), /* @__PURE__ */ h(r, { mode: j.mode })]
116
+ }), /* @__PURE__ */ g("p", {
81
117
  className: "text-sm text-muted-foreground",
82
118
  children: [
83
- S.name,
119
+ M.name,
84
120
  " - ",
85
- t(S.planningFocus)
121
+ i(M.planningFocus)
86
122
  ]
87
123
  })] }),
88
- /* @__PURE__ */ f("div", {
124
+ /* @__PURE__ */ g("div", {
89
125
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
90
126
  children: [
91
- /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
127
+ /* @__PURE__ */ h(v, { children: /* @__PURE__ */ g(y, {
92
128
  className: "pt-4",
93
129
  children: [
94
- /* @__PURE__ */ f("div", {
130
+ /* @__PURE__ */ g("div", {
95
131
  className: "flex items-center gap-2 text-muted-foreground text-xs",
96
- children: [/* @__PURE__ */ d(o, { className: "h-4 w-4" }), "Open items on track"]
132
+ children: [/* @__PURE__ */ h(u, { className: "h-4 w-4" }), "Open items on track"]
97
133
  }),
98
- /* @__PURE__ */ d("p", {
134
+ /* @__PURE__ */ h("p", {
99
135
  className: "text-2xl font-bold mt-1",
100
- children: P === null ? "—" : `${P}%`
136
+ children: W === null ? "—" : `${W}%`
101
137
  }),
102
- /* @__PURE__ */ d("div", {
138
+ /* @__PURE__ */ h("div", {
103
139
  className: "w-full h-2 rounded-full bg-muted mt-2 overflow-hidden",
104
- children: /* @__PURE__ */ d("div", {
140
+ children: /* @__PURE__ */ h("div", {
105
141
  className: "h-full rounded-full bg-success",
106
- style: { width: `${P ?? 0}%` }
142
+ style: { width: `${W ?? 0}%` }
107
143
  })
108
144
  })
109
145
  ]
110
146
  }) }),
111
- /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
147
+ /* @__PURE__ */ h(v, { children: /* @__PURE__ */ g(y, {
112
148
  className: "pt-4",
113
149
  children: [
114
- /* @__PURE__ */ f("div", {
150
+ /* @__PURE__ */ g("div", {
115
151
  className: "flex items-center gap-2 text-muted-foreground text-xs",
116
- children: [/* @__PURE__ */ d(s, { className: "h-4 w-4" }), "Progress"]
152
+ children: [/* @__PURE__ */ h(d, { className: "h-4 w-4" }), "Progress"]
117
153
  }),
118
- /* @__PURE__ */ f("p", {
154
+ /* @__PURE__ */ g("p", {
119
155
  className: "text-2xl font-bold mt-1",
120
- children: [D, "%"]
156
+ children: [L, "%"]
121
157
  }),
122
- /* @__PURE__ */ f("p", {
158
+ /* @__PURE__ */ g("p", {
123
159
  className: "text-xs text-muted-foreground mt-1",
124
- children: [C.length, " items total"]
160
+ children: [N.length, " items total"]
125
161
  })
126
162
  ]
127
163
  }) }),
128
- /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
164
+ /* @__PURE__ */ h(v, { children: /* @__PURE__ */ g(y, {
129
165
  className: "pt-4",
130
166
  children: [
131
- /* @__PURE__ */ f("div", {
167
+ /* @__PURE__ */ g("div", {
132
168
  className: "flex items-center gap-2 text-muted-foreground text-xs",
133
- children: [/* @__PURE__ */ d(i, { className: "h-4 w-4" }), "Completed"]
169
+ children: [/* @__PURE__ */ h(c, { className: "h-4 w-4" }), "Completed"]
134
170
  }),
135
- /* @__PURE__ */ d("p", {
171
+ /* @__PURE__ */ h("p", {
136
172
  className: "text-2xl font-bold mt-1",
137
- children: E.length
173
+ children: I.length
138
174
  }),
139
- /* @__PURE__ */ d("p", {
175
+ /* @__PURE__ */ h("p", {
140
176
  className: "text-xs text-muted-foreground mt-1",
141
177
  children: "items completed"
142
178
  })
143
179
  ]
144
180
  }) }),
145
- /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
181
+ /* @__PURE__ */ h(v, { children: /* @__PURE__ */ g(y, {
146
182
  className: "pt-4",
147
183
  children: [
148
- /* @__PURE__ */ f("div", {
184
+ /* @__PURE__ */ g("div", {
149
185
  className: "flex items-center gap-2 text-muted-foreground text-xs",
150
- children: [/* @__PURE__ */ d(n, { className: "h-4 w-4" }), "Overdue"]
186
+ children: [/* @__PURE__ */ h(o, { className: "h-4 w-4" }), "Overdue"]
151
187
  }),
152
- /* @__PURE__ */ d("p", {
188
+ /* @__PURE__ */ h("p", {
153
189
  className: "text-2xl font-bold mt-1",
154
- children: M.length
190
+ children: H.length
155
191
  }),
156
- /* @__PURE__ */ d("p", {
192
+ /* @__PURE__ */ h("p", {
157
193
  className: "text-xs text-muted-foreground mt-1",
158
194
  children: "items past end date"
159
195
  })
@@ -161,53 +197,53 @@ function v() {
161
197
  }) })
162
198
  ]
163
199
  }),
164
- /* @__PURE__ */ f("div", {
200
+ /* @__PURE__ */ g("div", {
165
201
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
166
- children: [/* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ f(g, {
202
+ children: [/* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ g(x, {
167
203
  className: "flex items-center gap-2",
168
- children: [/* @__PURE__ */ d(r, { className: "h-4 w-4" }), "Item Velocity"]
169
- }) }), /* @__PURE__ */ f(m, { children: [I.length === 0 ? /* @__PURE__ */ d("p", {
204
+ children: [/* @__PURE__ */ h(s, { className: "h-4 w-4" }), "Item Velocity"]
205
+ }) }), /* @__PURE__ */ g(y, { children: [K.length === 0 ? /* @__PURE__ */ h("p", {
170
206
  className: "text-sm text-muted-foreground text-center py-8",
171
207
  children: "No timeline data available"
172
- }) : /* @__PURE__ */ d("div", {
208
+ }) : /* @__PURE__ */ h("div", {
173
209
  className: "flex items-end gap-2 h-48",
174
- children: I.map((e) => /* @__PURE__ */ f("div", {
210
+ children: K.map((e) => /* @__PURE__ */ g("div", {
175
211
  className: "flex-1 flex flex-col items-center gap-1",
176
- children: [/* @__PURE__ */ f("div", {
212
+ children: [/* @__PURE__ */ g("div", {
177
213
  className: "flex gap-0.5 items-end w-full justify-center h-40",
178
- children: [/* @__PURE__ */ d("div", {
214
+ children: [/* @__PURE__ */ h("div", {
179
215
  className: "w-3 bg-primary/30 rounded-t",
180
- style: { height: `${e.planned / L * 100}%` },
216
+ style: { height: `${e.planned / q * 100}%` },
181
217
  title: `Planned: ${e.planned}`
182
- }), /* @__PURE__ */ d("div", {
218
+ }), /* @__PURE__ */ h("div", {
183
219
  className: "w-3 bg-primary rounded-t",
184
- style: { height: `${e.completed / L * 100}%` },
220
+ style: { height: `${e.completed / q * 100}%` },
185
221
  title: `Completed: ${e.completed}`
186
222
  })]
187
- }), /* @__PURE__ */ d("span", {
223
+ }), /* @__PURE__ */ h("span", {
188
224
  className: "text-xs text-muted-foreground",
189
225
  children: e.month
190
226
  })]
191
227
  }, e.month))
192
- }), /* @__PURE__ */ f("div", {
228
+ }), /* @__PURE__ */ g("div", {
193
229
  className: "flex gap-4 justify-center mt-3 text-xs",
194
- children: [/* @__PURE__ */ f("span", {
230
+ children: [/* @__PURE__ */ g("span", {
195
231
  className: "flex items-center gap-1",
196
- children: [/* @__PURE__ */ d("span", { className: "h-2 w-2 rounded bg-primary/30" }), "Planned"]
197
- }), /* @__PURE__ */ f("span", {
232
+ children: [/* @__PURE__ */ h("span", { className: "h-2 w-2 rounded bg-primary/30" }), "Planned"]
233
+ }), /* @__PURE__ */ g("span", {
198
234
  className: "flex items-center gap-1",
199
- children: [/* @__PURE__ */ d("span", { className: "h-2 w-2 rounded bg-primary" }), "Completed"]
235
+ children: [/* @__PURE__ */ h("span", { className: "h-2 w-2 rounded bg-primary" }), "Completed"]
200
236
  })]
201
- })] })] }), /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Item Status Breakdown" }) }), /* @__PURE__ */ d(m, {
237
+ })] })] }), /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Item Status Breakdown" }) }), /* @__PURE__ */ h(y, {
202
238
  className: "space-y-3",
203
- children: Object.entries(O).map(([e, t]) => {
204
- let n = C.length, r = n > 0 ? Math.round(t / n * 100) : 0;
205
- return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
239
+ children: Object.entries(R).map(([e, t]) => {
240
+ let n = N.length, r = n > 0 ? Math.round(t / n * 100) : 0;
241
+ return /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ g("div", {
206
242
  className: "flex justify-between text-sm mb-1",
207
- children: [/* @__PURE__ */ d("span", {
243
+ children: [/* @__PURE__ */ h("span", {
208
244
  className: "capitalize",
209
245
  children: e.toLowerCase().replace("_", " ")
210
- }), /* @__PURE__ */ f("span", {
246
+ }), /* @__PURE__ */ g("span", {
211
247
  className: "font-medium",
212
248
  children: [
213
249
  t,
@@ -216,9 +252,9 @@ function v() {
216
252
  "%)"
217
253
  ]
218
254
  })]
219
- }), /* @__PURE__ */ d("div", {
255
+ }), /* @__PURE__ */ h("div", {
220
256
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
221
- children: /* @__PURE__ */ d("div", {
257
+ children: /* @__PURE__ */ h("div", {
222
258
  className: `h-full rounded-full ${{
223
259
  TODO: "bg-gray-400",
224
260
  PLANNED: "bg-yellow-400",
@@ -233,21 +269,21 @@ function v() {
233
269
  })
234
270
  })] })]
235
271
  }),
236
- /* @__PURE__ */ f("div", {
272
+ /* @__PURE__ */ g("div", {
237
273
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
238
- children: [/* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Feature request status (workspace)" }) }), /* @__PURE__ */ d(m, {
274
+ children: [/* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Feature request status (workspace)" }) }), /* @__PURE__ */ h(y, {
239
275
  className: "space-y-3",
240
- children: Object.entries(k).length === 0 ? /* @__PURE__ */ d("p", {
276
+ children: Object.entries(z).length === 0 ? /* @__PURE__ */ h("p", {
241
277
  className: "text-sm text-muted-foreground text-center py-4",
242
278
  children: "No feature request data"
243
- }) : Object.entries(k).map(([e, t]) => {
244
- let n = x?.all ?? 0, r = n > 0 ? Math.round(t / n * 100) : 0;
245
- return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
279
+ }) : Object.entries(z).map(([e, t]) => {
280
+ let n = O?.all ?? 0, r = n > 0 ? Math.round(t / n * 100) : 0;
281
+ return /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ g("div", {
246
282
  className: "flex justify-between text-sm mb-1",
247
- children: [/* @__PURE__ */ d("span", {
283
+ children: [/* @__PURE__ */ h("span", {
248
284
  className: "capitalize",
249
285
  children: e.toLowerCase().replace("_", " ")
250
- }), /* @__PURE__ */ f("span", {
286
+ }), /* @__PURE__ */ g("span", {
251
287
  className: "font-medium",
252
288
  children: [
253
289
  t,
@@ -256,9 +292,9 @@ function v() {
256
292
  "%)"
257
293
  ]
258
294
  })]
259
- }), /* @__PURE__ */ d("div", {
295
+ }), /* @__PURE__ */ h("div", {
260
296
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
261
- children: /* @__PURE__ */ d("div", {
297
+ children: /* @__PURE__ */ h("div", {
262
298
  className: `h-full rounded-full ${{
263
299
  SUBMITTED: "bg-gray-400",
264
300
  UNDER_REVIEW: "bg-yellow-400",
@@ -271,16 +307,16 @@ function v() {
271
307
  })
272
308
  })] }, e);
273
309
  })
274
- })] }), /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Swimlane Distribution" }) }), /* @__PURE__ */ d(m, {
310
+ })] }), /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Swimlane Distribution" }) }), /* @__PURE__ */ h(y, {
275
311
  className: "space-y-3",
276
- children: Object.entries(A).length === 0 ? /* @__PURE__ */ d("p", {
312
+ children: Object.entries(B).length === 0 ? /* @__PURE__ */ h("p", {
277
313
  className: "text-sm text-muted-foreground text-center py-4",
278
314
  children: "No swimlane data"
279
- }) : Object.entries(A).map(([e, t]) => {
280
- let n = C.length, r = n > 0 ? Math.round(t / n * 100) : 0;
281
- return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
315
+ }) : Object.entries(B).map(([e, t]) => {
316
+ let n = N.length, r = n > 0 ? Math.round(t / n * 100) : 0;
317
+ return /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ g("div", {
282
318
  className: "flex justify-between text-sm mb-1",
283
- children: [/* @__PURE__ */ d("span", { children: e }), /* @__PURE__ */ f("span", {
319
+ children: [/* @__PURE__ */ h("span", { children: e }), /* @__PURE__ */ g("span", {
284
320
  className: "font-medium",
285
321
  children: [
286
322
  t,
@@ -289,9 +325,9 @@ function v() {
289
325
  "%)"
290
326
  ]
291
327
  })]
292
- }), /* @__PURE__ */ d("div", {
328
+ }), /* @__PURE__ */ h("div", {
293
329
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
294
- children: /* @__PURE__ */ d("div", {
330
+ children: /* @__PURE__ */ h("div", {
295
331
  className: "h-full rounded-full bg-primary",
296
332
  style: { width: `${r}%` }
297
333
  })
@@ -299,17 +335,17 @@ function v() {
299
335
  })
300
336
  })] })]
301
337
  }),
302
- M.length > 0 && /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Overdue Items" }) }), /* @__PURE__ */ d(m, { children: /* @__PURE__ */ d("div", {
338
+ H.length > 0 && /* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Overdue Items" }) }), /* @__PURE__ */ h(y, { children: /* @__PURE__ */ h("div", {
303
339
  className: "space-y-3",
304
- children: M.map((e) => /* @__PURE__ */ f("div", {
340
+ children: H.map((e) => /* @__PURE__ */ g("div", {
305
341
  className: "flex items-center justify-between border rounded-md p-3",
306
- children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
342
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
307
343
  className: "text-sm font-medium",
308
344
  children: e.title
309
- }), /* @__PURE__ */ f("p", {
345
+ }), /* @__PURE__ */ g("p", {
310
346
  className: "text-xs text-muted-foreground",
311
347
  children: ["Due: ", e.endDate ? new Date(e.endDate).toLocaleDateString() : "N/A"]
312
- })] }), /* @__PURE__ */ d("span", {
348
+ })] }), /* @__PURE__ */ h("span", {
313
349
  className: "text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize",
314
350
  children: e.status.toLowerCase().replace("_", " ")
315
351
  })]
@@ -319,6 +355,6 @@ function v() {
319
355
  });
320
356
  }
321
357
  //#endregion
322
- export { v as ProductsRoadmapAnalyticsPagePage };
358
+ export { w as ProductsRoadmapAnalyticsPagePage };
323
359
 
324
360
  //# sourceMappingURL=ProductsRoadmapAnalyticsPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsRoadmapAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.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 } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, Target, CheckCircle, AlertTriangle, BarChart3, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { useFeatureCounts } from '../hooks/useCounts';\nimport { planningFocusLabel } from '../roadmaps/planningFocus';\n\nconst GET_ROADMAP_ANALYTICS = gql`\n query GetRoadmapAnalytics($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n status\n startDate\n endDate\n swimlane\n }\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n status: string;\n startDate: string | null;\n endDate: string | null;\n swimlane: string | null;\n}\n\nexport function ProductsRoadmapAnalyticsPagePage() {\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_ANALYTICS, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const featureCounts = useFeatureCounts();\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 analytics: {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\n // BOFF-7994: cancelled items no longer need doing — out of the progress denominator and out of \"overdue\".\n const isDone = (i: RoadmapItemNode) => i.status === 'COMPLETED' || i.status === 'DONE';\n const liveItems = items.filter((i) => i.status !== 'CANCELLED');\n const completedItems = liveItems.filter(isDone);\n const progress =\n liveItems.length > 0 ? Math.round((completedItems.length / liveItems.length) * 100) : 0;\n\n // Items by status\n const itemsByStatus: Record<string, number> = {};\n for (const item of items) {\n itemsByStatus[item.status] = (itemsByStatus[item.status] || 0) + 1;\n }\n\n // Feature request status across the WORKSPACE, from server counts (it was the 100 newest, counted in the browser).\n const featuresByStatus: Record<string, number> = featureCounts\n ? Object.fromEntries(Object.entries(featureCounts.byStatus).filter(([, n]) => n > 0))\n : {};\n\n // Items by swimlane\n const itemsBySwimlane: Record<string, number> = {};\n for (const item of items) {\n const lane = item.swimlane ?? 'Uncategorized';\n itemsBySwimlane[lane] = (itemsBySwimlane[lane] || 0) + 1;\n }\n\n // Items with overdue end dates\n const now = new Date();\n const overdueItems = liveItems.filter(\n (i) => i.endDate && new Date(i.endDate) < now && !isDone(i)\n );\n\n // \"On track\" replaces an undefined health score (0.7 x progress + 30 x not-overdue share, so an empty roadmap\n // scored 30/100): the share of OPEN items that are neither overdue nor flagged at risk or delayed.\n const openItems = liveItems.filter((i) => !isDone(i));\n const onTrack =\n openItems.length > 0\n ? Math.round(\n (openItems.filter(\n (i) => !overdueItems.includes(i) && i.status !== 'AT_RISK' && i.status !== 'DELAYED'\n ).length /\n openItems.length) *\n 100\n )\n : null;\n\n // Items due per month, keyed by YYYY-MM and sorted by DATE, the latest six kept (they were in storage order,\n // and the last six inserted were kept — out of order, recent months dropped).\n const velocityMap: Record<string, { planned: number; completed: number }> = {};\n for (const item of liveItems) {\n if (item.endDate) {\n const key = item.endDate.slice(0, 7);\n if (!velocityMap[key]) velocityMap[key] = { planned: 0, completed: 0 };\n velocityMap[key].planned += 1;\n if (isDone(item)) velocityMap[key].completed += 1;\n }\n }\n const velocityData = Object.entries(velocityMap)\n .sort(([a], [b]) => a.localeCompare(b))\n .slice(-6)\n .map(([key, vals]) => ({\n month: new Date(`${key}-01T00:00:00Z`).toLocaleDateString('en-US', {\n month: 'short',\n year: '2-digit',\n timeZone: 'UTC',\n }),\n ...vals,\n }));\n const maxVal = Math.max(...velocityData.map((d) => Math.max(d.planned, d.completed)), 1);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Roadmap Analytics</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.name} - {planningFocusLabel(roadmap.planningFocus)}\n </p>\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 Open items on track\n </div>\n <p className=\"text-2xl font-bold mt-1\">{onTrack === null ? '—' : `${onTrack}%`}</p>\n <div className=\"w-full h-2 rounded-full bg-muted mt-2 overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-success\"\n style={{ width: `${onTrack ?? 0}%` }}\n />\n </div>\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 <TrendingUp className=\"h-4 w-4\" />\n Progress\n </div>\n <p className=\"text-2xl font-bold mt-1\">{progress}%</p>\n <p className=\"text-xs text-muted-foreground mt-1\">{items.length} items total</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 <CheckCircle className=\"h-4 w-4\" />\n Completed\n </div>\n <p className=\"text-2xl font-bold mt-1\">{completedItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items completed</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 <AlertTriangle className=\"h-4 w-4\" />\n Overdue\n </div>\n <p className=\"text-2xl font-bold mt-1\">{overdueItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items past end date</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Item Velocity\n </CardTitle>\n </CardHeader>\n <CardContent>\n {velocityData.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No timeline data available\n </p>\n ) : (\n <div className=\"flex items-end gap-2 h-48\">\n {velocityData.map((d) => (\n <div key={d.month} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"flex gap-0.5 items-end w-full justify-center h-40\">\n <div\n className=\"w-3 bg-primary/30 rounded-t\"\n style={{ height: `${(d.planned / maxVal) * 100}%` }}\n title={`Planned: ${d.planned}`}\n />\n <div\n className=\"w-3 bg-primary rounded-t\"\n style={{ height: `${(d.completed / maxVal) * 100}%` }}\n title={`Completed: ${d.completed}`}\n />\n </div>\n <span className=\"text-xs text-muted-foreground\">{d.month}</span>\n </div>\n ))}\n </div>\n )}\n <div className=\"flex gap-4 justify-center mt-3 text-xs\">\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary/30\" />\n Planned\n </span>\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary\" />\n Completed\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Item Status Breakdown</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsByStatus).map(([status, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n TODO: 'bg-gray-400',\n PLANNED: 'bg-yellow-400',\n IN_PROGRESS: 'bg-blue-400',\n BLOCKED: 'bg-red-400',\n DONE: 'bg-green-400',\n COMPLETED: 'bg-green-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature request status (workspace)</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(featuresByStatus).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No feature request data\n </p>\n ) : (\n Object.entries(featuresByStatus).map(([status, count]) => {\n const total = featureCounts?.all ?? 0;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Swimlane Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsBySwimlane).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">No swimlane data</p>\n ) : (\n Object.entries(itemsBySwimlane).map(([lane, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n return (\n <div key={lane}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>{lane}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </CardContent>\n </Card>\n </div>\n\n {overdueItems.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Overdue Items</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {overdueItems.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 <p className=\"text-xs text-muted-foreground\">\n Due: {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'N/A'}\n </p>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize\">\n {item.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsRoadmapAnalyticsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { TrendingUp, Target, CheckCircle, AlertTriangle, BarChart3, Loader2 } from 'lucide-react';\nimport { useParams } from 'react-router';\nimport { useFeatureCounts } from '../hooks/useCounts';\nimport { planningFocusLabel } from '../roadmaps/planningFocus';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { useLiveRefresh, type LiveEvent } from '../hooks/useLiveRefresh';\nimport { LiveStatus } from '../controls/LiveStatus';\n\n// PRM-11: this page's one query — re-read on any roadmap- or item-level event, same shape as the detail page's\n// and the Now/Next/Later board's own `useLiveRefresh` calls (each page/component is its own self-contained\n// subscriber, over its own named query).\nconst ROADMAP_ANALYTICS_QUERIES = ['GetRoadmapAnalytics'];\n\nconst GET_ROADMAP_ANALYTICS = gql`\n query GetRoadmapAnalytics($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n status\n startDate\n endDate\n swimlane\n }\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n status: string;\n startDate: string | null;\n endDate: string | null;\n swimlane: string | null;\n}\n\nexport function ProductsRoadmapAnalyticsPagePage() {\n const params = useParams<{ id: string }>();\n const roadmapId = params.id ?? '';\n const navigate = usePlanMagnetNavigate();\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_ANALYTICS, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const featureCounts = useFeatureCounts();\n\n // PRM-11: once a DELETED event names the roadmap itself, show the clear \"This roadmap was deleted\" state below\n // instead of leaving the charts on stale data — see the same hook/comment on `ProductsRoadmapDetailPagePage`.\n const [roadmapDeleted, setRoadmapDeleted] = useState(false);\n const roadmapLive = useLiveRefresh({\n kind: 'roadmap',\n roadmapId,\n queries: ROADMAP_ANALYTICS_QUERIES,\n onEvent: (event: LiveEvent) => {\n if (event.kind === 'DELETED' && event.id === roadmapId) setRoadmapDeleted(true);\n },\n });\n\n if (roadmapDeleted) {\n return (\n <div className=\"space-y-3 p-6\" data-testid=\"roadmap-deleted\">\n <p className=\"text-sm text-muted-foreground\">This roadmap was deleted.</p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/products/roadmaps-page')}>\n Back to roadmaps\n </Button>\n </div>\n );\n }\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load analytics: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) {\n return (\n <div className=\"space-y-3 p-6\" data-testid=\"roadmap-not-found\">\n <p className=\"text-sm text-muted-foreground\">\n This roadmap does not exist in this workspace, or it was deleted.\n </p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/products/roadmaps-page')}>\n Back to roadmaps\n </Button>\n </div>\n );\n }\n\n const items: RoadmapItemNode[] = roadmap.items ?? [];\n\n // BOFF-7994: cancelled items no longer need doing — out of the progress denominator and out of \"overdue\".\n const isDone = (i: RoadmapItemNode) => i.status === 'COMPLETED' || i.status === 'DONE';\n const liveItems = items.filter((i) => i.status !== 'CANCELLED');\n const completedItems = liveItems.filter(isDone);\n const progress =\n liveItems.length > 0 ? Math.round((completedItems.length / liveItems.length) * 100) : 0;\n\n // Items by status\n const itemsByStatus: Record<string, number> = {};\n for (const item of items) {\n itemsByStatus[item.status] = (itemsByStatus[item.status] || 0) + 1;\n }\n\n // Feature request status across the WORKSPACE, from server counts (it was the 100 newest, counted in the browser).\n const featuresByStatus: Record<string, number> = featureCounts\n ? Object.fromEntries(Object.entries(featureCounts.byStatus).filter(([, n]) => n > 0))\n : {};\n\n // Items by swimlane\n const itemsBySwimlane: Record<string, number> = {};\n for (const item of items) {\n const lane = item.swimlane ?? 'Uncategorized';\n itemsBySwimlane[lane] = (itemsBySwimlane[lane] || 0) + 1;\n }\n\n // Items with overdue end dates\n const now = new Date();\n const overdueItems = liveItems.filter(\n (i) => i.endDate && new Date(i.endDate) < now && !isDone(i)\n );\n\n // \"On track\" replaces an undefined health score (0.7 x progress + 30 x not-overdue share, so an empty roadmap\n // scored 30/100): the share of OPEN items that are neither overdue nor flagged at risk or delayed.\n const openItems = liveItems.filter((i) => !isDone(i));\n const onTrack =\n openItems.length > 0\n ? Math.round(\n (openItems.filter(\n (i) => !overdueItems.includes(i) && i.status !== 'AT_RISK' && i.status !== 'DELAYED'\n ).length /\n openItems.length) *\n 100\n )\n : null;\n\n // Items due per month, keyed by YYYY-MM and sorted by DATE, the latest six kept (they were in storage order,\n // and the last six inserted were kept — out of order, recent months dropped).\n const velocityMap: Record<string, { planned: number; completed: number }> = {};\n for (const item of liveItems) {\n if (item.endDate) {\n const key = item.endDate.slice(0, 7);\n if (!velocityMap[key]) velocityMap[key] = { planned: 0, completed: 0 };\n velocityMap[key].planned += 1;\n if (isDone(item)) velocityMap[key].completed += 1;\n }\n }\n const velocityData = Object.entries(velocityMap)\n .sort(([a], [b]) => a.localeCompare(b))\n .slice(-6)\n .map(([key, vals]) => ({\n month: new Date(`${key}-01T00:00:00Z`).toLocaleDateString('en-US', {\n month: 'short',\n year: '2-digit',\n timeZone: 'UTC',\n }),\n ...vals,\n }));\n const maxVal = Math.max(...velocityData.map((d) => Math.max(d.planned, d.completed)), 1);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h1 className=\"text-2xl font-semibold\">Roadmap Analytics</h1>\n <LiveStatus mode={roadmapLive.mode} />\n </div>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.name} - {planningFocusLabel(roadmap.planningFocus)}\n </p>\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 Open items on track\n </div>\n <p className=\"text-2xl font-bold mt-1\">{onTrack === null ? '—' : `${onTrack}%`}</p>\n <div className=\"w-full h-2 rounded-full bg-muted mt-2 overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-success\"\n style={{ width: `${onTrack ?? 0}%` }}\n />\n </div>\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 <TrendingUp className=\"h-4 w-4\" />\n Progress\n </div>\n <p className=\"text-2xl font-bold mt-1\">{progress}%</p>\n <p className=\"text-xs text-muted-foreground mt-1\">{items.length} items total</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 <CheckCircle className=\"h-4 w-4\" />\n Completed\n </div>\n <p className=\"text-2xl font-bold mt-1\">{completedItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items completed</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 <AlertTriangle className=\"h-4 w-4\" />\n Overdue\n </div>\n <p className=\"text-2xl font-bold mt-1\">{overdueItems.length}</p>\n <p className=\"text-xs text-muted-foreground mt-1\">items past end date</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4\" />\n Item Velocity\n </CardTitle>\n </CardHeader>\n <CardContent>\n {velocityData.length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-8\">\n No timeline data available\n </p>\n ) : (\n <div className=\"flex items-end gap-2 h-48\">\n {velocityData.map((d) => (\n <div key={d.month} className=\"flex-1 flex flex-col items-center gap-1\">\n <div className=\"flex gap-0.5 items-end w-full justify-center h-40\">\n <div\n className=\"w-3 bg-primary/30 rounded-t\"\n style={{ height: `${(d.planned / maxVal) * 100}%` }}\n title={`Planned: ${d.planned}`}\n />\n <div\n className=\"w-3 bg-primary rounded-t\"\n style={{ height: `${(d.completed / maxVal) * 100}%` }}\n title={`Completed: ${d.completed}`}\n />\n </div>\n <span className=\"text-xs text-muted-foreground\">{d.month}</span>\n </div>\n ))}\n </div>\n )}\n <div className=\"flex gap-4 justify-center mt-3 text-xs\">\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary/30\" />\n Planned\n </span>\n <span className=\"flex items-center gap-1\">\n <span className=\"h-2 w-2 rounded bg-primary\" />\n Completed\n </span>\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Item Status Breakdown</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsByStatus).map(([status, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n TODO: 'bg-gray-400',\n PLANNED: 'bg-yellow-400',\n IN_PROGRESS: 'bg-blue-400',\n BLOCKED: 'bg-red-400',\n DONE: 'bg-green-400',\n COMPLETED: 'bg-green-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature request status (workspace)</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(featuresByStatus).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No feature request data\n </p>\n ) : (\n Object.entries(featuresByStatus).map(([status, count]) => {\n const total = featureCounts?.all ?? 0;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n const colors: Record<string, string> = {\n SUBMITTED: 'bg-gray-400',\n UNDER_REVIEW: 'bg-yellow-400',\n PLANNED: 'bg-blue-400',\n IN_PROGRESS: 'bg-purple-400',\n COMPLETED: 'bg-green-400',\n DECLINED: 'bg-red-400',\n };\n return (\n <div key={status}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span className=\"capitalize\">{status.toLowerCase().replace('_', ' ')}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${colors[status] ?? 'bg-gray-300'}`}\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Swimlane Distribution</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {Object.entries(itemsBySwimlane).length === 0 ? (\n <p className=\"text-sm text-muted-foreground text-center py-4\">No swimlane data</p>\n ) : (\n Object.entries(itemsBySwimlane).map(([lane, count]) => {\n const total = items.length;\n const pct = total > 0 ? Math.round((count / total) * 100) : 0;\n return (\n <div key={lane}>\n <div className=\"flex justify-between text-sm mb-1\">\n <span>{lane}</span>\n <span className=\"font-medium\">\n {count} ({pct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full rounded-full bg-primary\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n );\n })\n )}\n </CardContent>\n </Card>\n </div>\n\n {overdueItems.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Overdue Items</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"space-y-3\">\n {overdueItems.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 <p className=\"text-xs text-muted-foreground\">\n Due: {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'N/A'}\n </p>\n </div>\n <span className=\"text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize\">\n {item.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n ))}\n </div>\n </CardContent>\n </Card>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsRoadmapDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"names":[],"mappings":"AA4GA,wBAAgB,6BAA6B,4CA4R5C"}
1
+ {"version":3,"file":"ProductsRoadmapDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"names":[],"mappings":"AAkHA,wBAAgB,6BAA6B,4CAsU5C"}