@burdenoff/microfe-planmagnet 2026.925.18 → 2026.926.2

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 (92) hide show
  1. package/dist/generated/wspace-types.d.ts +61 -2
  2. package/dist/generated/wspace-types.d.ts.map +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoot.js +90 -79
  5. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  7. package/dist/planmagnet/PlanMagnetRoutes.js +124 -127
  8. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  9. package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
  10. package/dist/planmagnet/create/CreateDialogs.js +149 -124
  11. package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
  12. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  13. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +1 -1
  14. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  15. package/dist/planmagnet/pages/NotFoundPage.d.ts +6 -0
  16. package/dist/planmagnet/pages/NotFoundPage.d.ts.map +1 -0
  17. package/dist/planmagnet/pages/NotFoundPage.js +18 -0
  18. package/dist/planmagnet/pages/NotFoundPage.js.map +1 -0
  19. package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
  20. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +93 -98
  21. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
  22. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +72 -79
  24. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  26. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +112 -123
  27. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  28. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  29. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +103 -87
  30. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  32. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +92 -98
  33. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  34. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  35. package/dist/planmagnet/pages/ProductsProductsPagePage.js +88 -96
  36. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  38. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +116 -114
  39. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  40. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -88
  42. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +79 -79
  45. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +111 -91
  48. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +117 -97
  51. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +150 -154
  54. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +154 -117
  57. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +75 -31
  60. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +161 -115
  63. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  64. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +117 -102
  66. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +147 -147
  69. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/TeamTeamPagePage.js +77 -67
  72. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  73. package/dist/planmagnet/roadmaps/planningFocus.d.ts +23 -0
  74. package/dist/planmagnet/roadmaps/planningFocus.d.ts.map +1 -0
  75. package/dist/planmagnet/roadmaps/planningFocus.js +76 -0
  76. package/dist/planmagnet/roadmaps/planningFocus.js.map +1 -0
  77. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  78. package/dist/planmagnet/sprints/SprintControls.js +48 -29
  79. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  80. package/dist/planmagnet/sprints/SprintDates.d.ts +37 -0
  81. package/dist/planmagnet/sprints/SprintDates.d.ts.map +1 -0
  82. package/dist/planmagnet/sprints/SprintDates.js +114 -0
  83. package/dist/planmagnet/sprints/SprintDates.js.map +1 -0
  84. package/dist/planmagnet/utils/email.d.ts +9 -0
  85. package/dist/planmagnet/utils/email.d.ts.map +1 -0
  86. package/dist/planmagnet/utils/email.js +15 -0
  87. package/dist/planmagnet/utils/email.js.map +1 -0
  88. package/dist/planmagnet/utils/formValidation.d.ts +18 -0
  89. package/dist/planmagnet/utils/formValidation.d.ts.map +1 -0
  90. package/dist/planmagnet/utils/formValidation.js +17 -0
  91. package/dist/planmagnet/utils/formValidation.js.map +1 -0
  92. package/package.json +1 -1
@@ -1,18 +1,20 @@
1
1
  import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
- import { AlertTriangle as t, BarChart3 as n, CheckCircle as r, Loader2 as i, Target as a, TrendingUp as o } from "lucide-react";
3
- import { useParams as s } from "react-router";
4
- import { gql as c } from "@apollo/client";
5
- import { useQuery as l } from "@apollo/client/react";
6
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
- import { Card as f, CardContent as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
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";
8
9
  //#region src/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.tsx
9
- var g = c`
10
+ var _ = l`
10
11
  query GetRoadmapAnalytics($id: ID!) {
11
12
  planmagnetRoadmap(id: $id) {
12
13
  id
13
14
  name
14
15
  description
15
16
  type
17
+ planningFocus
16
18
  isPublic
17
19
  ownerId
18
20
  createdAt
@@ -27,7 +29,7 @@ var g = c`
27
29
  }
28
30
  }
29
31
  }
30
- `, _ = c`
32
+ `, v = l`
31
33
  query ListFeatureRequestsForRoadmapAnalytics(
32
34
  $workspaceId: ID!
33
35
  $pagination: PlanmagnetPaginationInput
@@ -44,137 +46,137 @@ var g = c`
44
46
  }
45
47
  }
46
48
  `;
47
- function v() {
48
- let { workspaceId: c } = e(), v = s().id ?? "", { data: y, loading: b, error: x } = l(g, {
49
- variables: { id: v },
50
- skip: !v
51
- }), { data: S } = l(_, {
49
+ function y() {
50
+ let { workspaceId: l } = e(), y = c().id ?? "", { data: b, loading: x, error: S } = u(_, {
51
+ variables: { id: y },
52
+ skip: !y
53
+ }), { data: C } = u(v, {
52
54
  variables: {
53
- workspaceId: c,
55
+ workspaceId: l,
54
56
  pagination: { first: 100 }
55
57
  },
56
- skip: !c
58
+ skip: !l
57
59
  });
58
- if (b && !y) return /* @__PURE__ */ u("div", {
60
+ if (x && !b) return /* @__PURE__ */ d("div", {
59
61
  className: "flex items-center justify-center p-12",
60
- children: /* @__PURE__ */ u(i, { className: "h-8 w-8 animate-spin text-muted-foreground" })
62
+ children: /* @__PURE__ */ d(a, { className: "h-8 w-8 animate-spin text-muted-foreground" })
61
63
  });
62
- if (x) return /* @__PURE__ */ d("div", {
64
+ if (S) return /* @__PURE__ */ f("div", {
63
65
  className: "p-6 text-destructive",
64
- children: ["Failed to load analytics: ", x.message]
66
+ children: ["Failed to load analytics: ", S.message]
65
67
  });
66
- let C = y?.planmagnetRoadmap;
67
- if (!C) return /* @__PURE__ */ u("div", {
68
+ let w = b?.planmagnetRoadmap;
69
+ if (!w) return /* @__PURE__ */ d("div", {
68
70
  className: "p-6 text-muted-foreground",
69
71
  children: "Roadmap not found."
70
72
  });
71
- let w = C.items ?? [], T = S?.planmagnetFeatureRequests?.items ?? [], E = w.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), D = w.length > 0 ? Math.round(E.length / w.length * 100) : 0, O = {};
72
- for (let e of w) O[e.status] = (O[e.status] || 0) + 1;
73
- let k = {};
73
+ let T = w.items ?? [], E = C?.planmagnetFeatureRequests?.items ?? [], D = T.filter((e) => e.status === "COMPLETED" || e.status === "DONE"), O = T.length > 0 ? Math.round(D.length / T.length * 100) : 0, k = {};
74
74
  for (let e of T) k[e.status] = (k[e.status] || 0) + 1;
75
75
  let A = {};
76
- for (let e of w) {
76
+ for (let e of E) A[e.status] = (A[e.status] || 0) + 1;
77
+ let j = {};
78
+ for (let e of T) {
77
79
  let t = e.swimlane ?? "Uncategorized";
78
- A[t] = (A[t] || 0) + 1;
80
+ j[t] = (j[t] || 0) + 1;
79
81
  }
80
- let j = /* @__PURE__ */ new Date(), M = w.filter((e) => e.endDate && new Date(e.endDate) < j && e.status !== "COMPLETED" && e.status !== "DONE"), N = Math.max(0, Math.round(D * .7 + (1 - M.length / Math.max(w.length, 1)) * 30)), P = {};
81
- for (let e of w) if (e.endDate) {
82
+ let M = /* @__PURE__ */ new Date(), N = T.filter((e) => e.endDate && new Date(e.endDate) < M && e.status !== "COMPLETED" && e.status !== "DONE"), P = Math.max(0, Math.round(O * .7 + (1 - N.length / Math.max(T.length, 1)) * 30)), F = {};
83
+ for (let e of T) if (e.endDate) {
82
84
  let t = new Date(e.endDate).toLocaleDateString("en-US", {
83
85
  month: "short",
84
86
  year: "2-digit"
85
87
  });
86
- P[t] || (P[t] = {
88
+ F[t] || (F[t] = {
87
89
  planned: 0,
88
90
  completed: 0
89
- }), P[t].planned += 1, (e.status === "COMPLETED" || e.status === "DONE") && (P[t].completed += 1);
91
+ }), F[t].planned += 1, (e.status === "COMPLETED" || e.status === "DONE") && (F[t].completed += 1);
90
92
  }
91
- let F = Object.entries(P).map(([e, t]) => ({
93
+ let I = Object.entries(F).map(([e, t]) => ({
92
94
  month: e,
93
95
  ...t
94
- })).slice(-6), I = Math.max(...F.map((e) => Math.max(e.planned, e.completed)), 1);
95
- return /* @__PURE__ */ d("div", {
96
+ })).slice(-6), L = Math.max(...I.map((e) => Math.max(e.planned, e.completed)), 1);
97
+ return /* @__PURE__ */ f("div", {
96
98
  className: "space-y-6 p-6",
97
99
  children: [
98
- /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
100
+ /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
99
101
  className: "text-2xl font-semibold",
100
102
  children: "Roadmap Analytics"
101
- }), /* @__PURE__ */ d("p", {
103
+ }), /* @__PURE__ */ f("p", {
102
104
  className: "text-sm text-muted-foreground",
103
105
  children: [
104
- C.name,
106
+ w.name,
105
107
  " - ",
106
- C.type.toLowerCase()
108
+ t(w.planningFocus)
107
109
  ]
108
110
  })] }),
109
- /* @__PURE__ */ d("div", {
111
+ /* @__PURE__ */ f("div", {
110
112
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
111
113
  children: [
112
- /* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
114
+ /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
113
115
  className: "pt-4",
114
116
  children: [
115
- /* @__PURE__ */ d("div", {
117
+ /* @__PURE__ */ f("div", {
116
118
  className: "flex items-center gap-2 text-muted-foreground text-xs",
117
- children: [/* @__PURE__ */ u(a, { className: "h-4 w-4" }), "Health Score"]
119
+ children: [/* @__PURE__ */ d(o, { className: "h-4 w-4" }), "Health Score"]
118
120
  }),
119
- /* @__PURE__ */ d("p", {
121
+ /* @__PURE__ */ f("p", {
120
122
  className: "text-2xl font-bold mt-1",
121
- children: [N, "/100"]
123
+ children: [P, "/100"]
122
124
  }),
123
- /* @__PURE__ */ u("div", {
125
+ /* @__PURE__ */ d("div", {
124
126
  className: "w-full h-2 rounded-full bg-muted mt-2 overflow-hidden",
125
- children: /* @__PURE__ */ u("div", {
127
+ children: /* @__PURE__ */ d("div", {
126
128
  className: "h-full rounded-full bg-green-500",
127
- style: { width: `${N}%` }
129
+ style: { width: `${P}%` }
128
130
  })
129
131
  })
130
132
  ]
131
133
  }) }),
132
- /* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
134
+ /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
133
135
  className: "pt-4",
134
136
  children: [
135
- /* @__PURE__ */ d("div", {
137
+ /* @__PURE__ */ f("div", {
136
138
  className: "flex items-center gap-2 text-muted-foreground text-xs",
137
- children: [/* @__PURE__ */ u(o, { className: "h-4 w-4" }), "Progress"]
139
+ children: [/* @__PURE__ */ d(s, { className: "h-4 w-4" }), "Progress"]
138
140
  }),
139
- /* @__PURE__ */ d("p", {
141
+ /* @__PURE__ */ f("p", {
140
142
  className: "text-2xl font-bold mt-1",
141
- children: [D, "%"]
143
+ children: [O, "%"]
142
144
  }),
143
- /* @__PURE__ */ d("p", {
145
+ /* @__PURE__ */ f("p", {
144
146
  className: "text-xs text-muted-foreground mt-1",
145
- children: [w.length, " items total"]
147
+ children: [T.length, " items total"]
146
148
  })
147
149
  ]
148
150
  }) }),
149
- /* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
151
+ /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
150
152
  className: "pt-4",
151
153
  children: [
152
- /* @__PURE__ */ d("div", {
154
+ /* @__PURE__ */ f("div", {
153
155
  className: "flex items-center gap-2 text-muted-foreground text-xs",
154
- children: [/* @__PURE__ */ u(r, { className: "h-4 w-4" }), "Completed"]
156
+ children: [/* @__PURE__ */ d(i, { className: "h-4 w-4" }), "Completed"]
155
157
  }),
156
- /* @__PURE__ */ u("p", {
158
+ /* @__PURE__ */ d("p", {
157
159
  className: "text-2xl font-bold mt-1",
158
- children: E.length
160
+ children: D.length
159
161
  }),
160
- /* @__PURE__ */ u("p", {
162
+ /* @__PURE__ */ d("p", {
161
163
  className: "text-xs text-muted-foreground mt-1",
162
164
  children: "items completed"
163
165
  })
164
166
  ]
165
167
  }) }),
166
- /* @__PURE__ */ u(f, { children: /* @__PURE__ */ d(p, {
168
+ /* @__PURE__ */ d(p, { children: /* @__PURE__ */ f(m, {
167
169
  className: "pt-4",
168
170
  children: [
169
- /* @__PURE__ */ d("div", {
171
+ /* @__PURE__ */ f("div", {
170
172
  className: "flex items-center gap-2 text-muted-foreground text-xs",
171
- children: [/* @__PURE__ */ u(t, { className: "h-4 w-4" }), "Overdue"]
173
+ children: [/* @__PURE__ */ d(n, { className: "h-4 w-4" }), "Overdue"]
172
174
  }),
173
- /* @__PURE__ */ u("p", {
175
+ /* @__PURE__ */ d("p", {
174
176
  className: "text-2xl font-bold mt-1",
175
- children: M.length
177
+ children: N.length
176
178
  }),
177
- /* @__PURE__ */ u("p", {
179
+ /* @__PURE__ */ d("p", {
178
180
  className: "text-xs text-muted-foreground mt-1",
179
181
  children: "items past end date"
180
182
  })
@@ -182,53 +184,53 @@ function v() {
182
184
  }) })
183
185
  ]
184
186
  }),
185
- /* @__PURE__ */ d("div", {
187
+ /* @__PURE__ */ f("div", {
186
188
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
187
- children: [/* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ d(h, {
189
+ children: [/* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ f(g, {
188
190
  className: "flex items-center gap-2",
189
- children: [/* @__PURE__ */ u(n, { className: "h-4 w-4" }), "Item Velocity"]
190
- }) }), /* @__PURE__ */ d(p, { children: [F.length === 0 ? /* @__PURE__ */ u("p", {
191
+ children: [/* @__PURE__ */ d(r, { className: "h-4 w-4" }), "Item Velocity"]
192
+ }) }), /* @__PURE__ */ f(m, { children: [I.length === 0 ? /* @__PURE__ */ d("p", {
191
193
  className: "text-sm text-muted-foreground text-center py-8",
192
194
  children: "No timeline data available"
193
- }) : /* @__PURE__ */ u("div", {
195
+ }) : /* @__PURE__ */ d("div", {
194
196
  className: "flex items-end gap-2 h-48",
195
- children: F.map((e) => /* @__PURE__ */ d("div", {
197
+ children: I.map((e) => /* @__PURE__ */ f("div", {
196
198
  className: "flex-1 flex flex-col items-center gap-1",
197
- children: [/* @__PURE__ */ d("div", {
199
+ children: [/* @__PURE__ */ f("div", {
198
200
  className: "flex gap-0.5 items-end w-full justify-center h-40",
199
- children: [/* @__PURE__ */ u("div", {
201
+ children: [/* @__PURE__ */ d("div", {
200
202
  className: "w-3 bg-primary/30 rounded-t",
201
- style: { height: `${e.planned / I * 100}%` },
203
+ style: { height: `${e.planned / L * 100}%` },
202
204
  title: `Planned: ${e.planned}`
203
- }), /* @__PURE__ */ u("div", {
205
+ }), /* @__PURE__ */ d("div", {
204
206
  className: "w-3 bg-primary rounded-t",
205
- style: { height: `${e.completed / I * 100}%` },
207
+ style: { height: `${e.completed / L * 100}%` },
206
208
  title: `Completed: ${e.completed}`
207
209
  })]
208
- }), /* @__PURE__ */ u("span", {
210
+ }), /* @__PURE__ */ d("span", {
209
211
  className: "text-xs text-muted-foreground",
210
212
  children: e.month
211
213
  })]
212
214
  }, e.month))
213
- }), /* @__PURE__ */ d("div", {
215
+ }), /* @__PURE__ */ f("div", {
214
216
  className: "flex gap-4 justify-center mt-3 text-xs",
215
- children: [/* @__PURE__ */ d("span", {
217
+ children: [/* @__PURE__ */ f("span", {
216
218
  className: "flex items-center gap-1",
217
- children: [/* @__PURE__ */ u("span", { className: "h-2 w-2 rounded bg-primary/30" }), "Planned"]
218
- }), /* @__PURE__ */ d("span", {
219
+ children: [/* @__PURE__ */ d("span", { className: "h-2 w-2 rounded bg-primary/30" }), "Planned"]
220
+ }), /* @__PURE__ */ f("span", {
219
221
  className: "flex items-center gap-1",
220
- children: [/* @__PURE__ */ u("span", { className: "h-2 w-2 rounded bg-primary" }), "Completed"]
222
+ children: [/* @__PURE__ */ d("span", { className: "h-2 w-2 rounded bg-primary" }), "Completed"]
221
223
  })]
222
- })] })] }), /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Item Status Breakdown" }) }), /* @__PURE__ */ u(p, {
224
+ })] })] }), /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Item Status Breakdown" }) }), /* @__PURE__ */ d(m, {
223
225
  className: "space-y-3",
224
- children: Object.entries(O).map(([e, t]) => {
225
- let n = w.length, r = n > 0 ? Math.round(t / n * 100) : 0;
226
- return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
226
+ children: Object.entries(k).map(([e, t]) => {
227
+ let n = T.length, r = n > 0 ? Math.round(t / n * 100) : 0;
228
+ return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
227
229
  className: "flex justify-between text-sm mb-1",
228
- children: [/* @__PURE__ */ u("span", {
230
+ children: [/* @__PURE__ */ d("span", {
229
231
  className: "capitalize",
230
232
  children: e.toLowerCase().replace("_", " ")
231
- }), /* @__PURE__ */ d("span", {
233
+ }), /* @__PURE__ */ f("span", {
232
234
  className: "font-medium",
233
235
  children: [
234
236
  t,
@@ -237,9 +239,9 @@ function v() {
237
239
  "%)"
238
240
  ]
239
241
  })]
240
- }), /* @__PURE__ */ u("div", {
242
+ }), /* @__PURE__ */ d("div", {
241
243
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
242
- children: /* @__PURE__ */ u("div", {
244
+ children: /* @__PURE__ */ d("div", {
243
245
  className: `h-full rounded-full ${{
244
246
  TODO: "bg-gray-400",
245
247
  PLANNED: "bg-yellow-400",
@@ -254,21 +256,21 @@ function v() {
254
256
  })
255
257
  })] })]
256
258
  }),
257
- /* @__PURE__ */ d("div", {
259
+ /* @__PURE__ */ f("div", {
258
260
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
259
- children: [/* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Feature Request Status" }) }), /* @__PURE__ */ u(p, {
261
+ children: [/* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Feature Request Status" }) }), /* @__PURE__ */ d(m, {
260
262
  className: "space-y-3",
261
- children: Object.entries(k).length === 0 ? /* @__PURE__ */ u("p", {
263
+ children: Object.entries(A).length === 0 ? /* @__PURE__ */ d("p", {
262
264
  className: "text-sm text-muted-foreground text-center py-4",
263
265
  children: "No feature request data"
264
- }) : Object.entries(k).map(([e, t]) => {
265
- let n = T.length, r = n > 0 ? Math.round(t / n * 100) : 0;
266
- return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
266
+ }) : Object.entries(A).map(([e, t]) => {
267
+ let n = E.length, r = n > 0 ? Math.round(t / n * 100) : 0;
268
+ return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
267
269
  className: "flex justify-between text-sm mb-1",
268
- children: [/* @__PURE__ */ u("span", {
270
+ children: [/* @__PURE__ */ d("span", {
269
271
  className: "capitalize",
270
272
  children: e.toLowerCase().replace("_", " ")
271
- }), /* @__PURE__ */ d("span", {
273
+ }), /* @__PURE__ */ f("span", {
272
274
  className: "font-medium",
273
275
  children: [
274
276
  t,
@@ -277,9 +279,9 @@ function v() {
277
279
  "%)"
278
280
  ]
279
281
  })]
280
- }), /* @__PURE__ */ u("div", {
282
+ }), /* @__PURE__ */ d("div", {
281
283
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
282
- children: /* @__PURE__ */ u("div", {
284
+ children: /* @__PURE__ */ d("div", {
283
285
  className: `h-full rounded-full ${{
284
286
  SUBMITTED: "bg-gray-400",
285
287
  UNDER_REVIEW: "bg-yellow-400",
@@ -292,16 +294,16 @@ function v() {
292
294
  })
293
295
  })] }, e);
294
296
  })
295
- })] }), /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Swimlane Distribution" }) }), /* @__PURE__ */ u(p, {
297
+ })] }), /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Swimlane Distribution" }) }), /* @__PURE__ */ d(m, {
296
298
  className: "space-y-3",
297
- children: Object.entries(A).length === 0 ? /* @__PURE__ */ u("p", {
299
+ children: Object.entries(j).length === 0 ? /* @__PURE__ */ d("p", {
298
300
  className: "text-sm text-muted-foreground text-center py-4",
299
301
  children: "No swimlane data"
300
- }) : Object.entries(A).map(([e, t]) => {
301
- let n = w.length, r = n > 0 ? Math.round(t / n * 100) : 0;
302
- return /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
302
+ }) : Object.entries(j).map(([e, t]) => {
303
+ let n = T.length, r = n > 0 ? Math.round(t / n * 100) : 0;
304
+ return /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ f("div", {
303
305
  className: "flex justify-between text-sm mb-1",
304
- children: [/* @__PURE__ */ u("span", { children: e }), /* @__PURE__ */ d("span", {
306
+ children: [/* @__PURE__ */ d("span", { children: e }), /* @__PURE__ */ f("span", {
305
307
  className: "font-medium",
306
308
  children: [
307
309
  t,
@@ -310,9 +312,9 @@ function v() {
310
312
  "%)"
311
313
  ]
312
314
  })]
313
- }), /* @__PURE__ */ u("div", {
315
+ }), /* @__PURE__ */ d("div", {
314
316
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
315
- children: /* @__PURE__ */ u("div", {
317
+ children: /* @__PURE__ */ d("div", {
316
318
  className: "h-full rounded-full bg-primary",
317
319
  style: { width: `${r}%` }
318
320
  })
@@ -320,17 +322,17 @@ function v() {
320
322
  })
321
323
  })] })]
322
324
  }),
323
- M.length > 0 && /* @__PURE__ */ d(f, { children: [/* @__PURE__ */ u(m, { children: /* @__PURE__ */ u(h, { children: "Overdue Items" }) }), /* @__PURE__ */ u(p, { children: /* @__PURE__ */ u("div", {
325
+ N.length > 0 && /* @__PURE__ */ f(p, { children: [/* @__PURE__ */ d(h, { children: /* @__PURE__ */ d(g, { children: "Overdue Items" }) }), /* @__PURE__ */ d(m, { children: /* @__PURE__ */ d("div", {
324
326
  className: "space-y-3",
325
- children: M.map((e) => /* @__PURE__ */ d("div", {
327
+ children: N.map((e) => /* @__PURE__ */ f("div", {
326
328
  className: "flex items-center justify-between border rounded-md p-3",
327
- children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
329
+ children: [/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("p", {
328
330
  className: "text-sm font-medium",
329
331
  children: e.title
330
- }), /* @__PURE__ */ d("p", {
332
+ }), /* @__PURE__ */ f("p", {
331
333
  className: "text-xs text-muted-foreground",
332
334
  children: ["Due: ", e.endDate ? new Date(e.endDate).toLocaleDateString() : "N/A"]
333
- })] }), /* @__PURE__ */ u("span", {
335
+ })] }), /* @__PURE__ */ d("span", {
334
336
  className: "text-xs px-2 py-0.5 rounded-full bg-red-100 text-red-800 capitalize",
335
337
  children: e.status.toLowerCase().replace("_", " ")
336
338
  })]
@@ -340,6 +342,6 @@ function v() {
340
342
  });
341
343
  }
342
344
  //#endregion
343
- export { v as ProductsRoadmapAnalyticsPagePage };
345
+ export { y as ProductsRoadmapAnalyticsPagePage };
344
346
 
345
347
  //# 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 { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\n\nconst GET_ROADMAP_ANALYTICS = gql`\n query GetRoadmapAnalytics($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n name\n description\n type\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\nconst LIST_FEATURE_REQUESTS_FOR_ANALYTICS = gql`\n query ListFeatureRequestsForRoadmapAnalytics(\n $workspaceId: ID!\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n status\n voteCount\n businessValue\n priorityScore\n }\n totalCount\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 { 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_ANALYTICS, {\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_ANALYTICS, {\n variables: { workspaceId, pagination: { first: 100 } },\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 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 const allFeatures = featuresData?.planmagnetFeatureRequests?.items ?? [];\n\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 // 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 status distribution\n const featuresByStatus: Record<string, number> = {};\n for (const f of allFeatures) {\n featuresByStatus[f.status] = (featuresByStatus[f.status] || 0) + 1;\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 = items.filter(\n (i) => i.endDate && new Date(i.endDate) < now && i.status !== 'COMPLETED' && i.status !== 'DONE'\n );\n\n // Health score based on progress and overdue items\n const healthScore = Math.max(\n 0,\n Math.round(progress * 0.7 + (1 - overdueItems.length / Math.max(items.length, 1)) * 30)\n );\n\n // Monthly velocity (items completed by month based on endDate)\n const velocityMap: Record<string, { planned: number; completed: number }> = {};\n for (const item of items) {\n if (item.endDate) {\n const month = new Date(item.endDate).toLocaleDateString('en-US', {\n month: 'short',\n year: '2-digit',\n });\n if (!velocityMap[month]) velocityMap[month] = { planned: 0, completed: 0 };\n velocityMap[month].planned += 1;\n if (item.status === 'COMPLETED' || item.status === 'DONE') {\n velocityMap[month].completed += 1;\n }\n }\n }\n const velocityData = Object.entries(velocityMap)\n .map(([month, vals]) => ({ month, ...vals }))\n .slice(-6);\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} - {roadmap.type.toLowerCase()}\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 Health Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{healthScore}/100</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-green-500\"\n style={{ width: `${healthScore}%` }}\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</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 = allFeatures.length;\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 { 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 { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\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\nconst LIST_FEATURE_REQUESTS_FOR_ANALYTICS = gql`\n query ListFeatureRequestsForRoadmapAnalytics(\n $workspaceId: ID!\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n status\n voteCount\n businessValue\n priorityScore\n }\n totalCount\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 { 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_ANALYTICS, {\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_ANALYTICS, {\n variables: { workspaceId, pagination: { first: 100 } },\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 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 const allFeatures = featuresData?.planmagnetFeatureRequests?.items ?? [];\n\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 // 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 status distribution\n const featuresByStatus: Record<string, number> = {};\n for (const f of allFeatures) {\n featuresByStatus[f.status] = (featuresByStatus[f.status] || 0) + 1;\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 = items.filter(\n (i) => i.endDate && new Date(i.endDate) < now && i.status !== 'COMPLETED' && i.status !== 'DONE'\n );\n\n // Health score based on progress and overdue items\n const healthScore = Math.max(\n 0,\n Math.round(progress * 0.7 + (1 - overdueItems.length / Math.max(items.length, 1)) * 30)\n );\n\n // Monthly velocity (items completed by month based on endDate)\n const velocityMap: Record<string, { planned: number; completed: number }> = {};\n for (const item of items) {\n if (item.endDate) {\n const month = new Date(item.endDate).toLocaleDateString('en-US', {\n month: 'short',\n year: '2-digit',\n });\n if (!velocityMap[month]) velocityMap[month] = { planned: 0, completed: 0 };\n velocityMap[month].planned += 1;\n if (item.status === 'COMPLETED' || item.status === 'DONE') {\n velocityMap[month].completed += 1;\n }\n }\n }\n const velocityData = Object.entries(velocityMap)\n .map(([month, vals]) => ({ month, ...vals }))\n .slice(-6);\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 Health Score\n </div>\n <p className=\"text-2xl font-bold mt-1\">{healthScore}/100</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-green-500\"\n style={{ width: `${healthScore}%` }}\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</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 = allFeatures.length;\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":"AA8DA,wBAAgB,6BAA6B,4CA6L5C"}
1
+ {"version":3,"file":"ProductsRoadmapDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapDetailPagePage.tsx"],"names":[],"mappings":"AAgEA,wBAAgB,6BAA6B,4CAyL5C"}