@burdenoff/microfe-planmagnet 2026.801.1 → 2026.827.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +92 -91
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +66 -65
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +71 -70
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,wBAAwB,4CAuMvC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import { useState as
|
|
3
|
-
import { ArrowUpDown as
|
|
4
|
-
import { gql as
|
|
5
|
-
import { useQuery as
|
|
6
|
-
import { jsx as
|
|
7
|
-
import { Badge as
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useState as n } from "react";
|
|
3
|
+
import { ArrowUpDown as r, Filter as i, Loader2 as a, Plus as o, Star as s, Target as c, ThumbsUp as l } from "lucide-react";
|
|
4
|
+
import { gql as u } from "@apollo/client";
|
|
5
|
+
import { useQuery as d } from "@apollo/client/react";
|
|
6
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
7
|
+
import { Badge as m, Button as h, Card as g, CardContent as _ } from "@burdenoff/fe-libs/ui";
|
|
8
8
|
//#region src/planmagnet/pages/ProductsFeaturesPagePage.tsx
|
|
9
|
-
var
|
|
9
|
+
var v = u`
|
|
10
10
|
query ListFeatureRequests(
|
|
11
11
|
$workspaceId: ID!
|
|
12
12
|
$filter: PlanmagnetFeatureRequestFilter
|
|
@@ -30,7 +30,7 @@ var _ = l`
|
|
|
30
30
|
totalCount
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
|
-
`,
|
|
33
|
+
`, y = {
|
|
34
34
|
SUBMITTED: "bg-gray-100 text-gray-800",
|
|
35
35
|
UNDER_REVIEW: "bg-yellow-100 text-yellow-800",
|
|
36
36
|
PLANNED: "bg-blue-100 text-blue-800",
|
|
@@ -39,27 +39,27 @@ var _ = l`
|
|
|
39
39
|
DECLINED: "bg-red-100 text-red-800",
|
|
40
40
|
DUPLICATE: "bg-orange-100 text-orange-800"
|
|
41
41
|
};
|
|
42
|
-
function
|
|
43
|
-
let { workspaceId:
|
|
42
|
+
function b() {
|
|
43
|
+
let u = t(), { workspaceId: b } = e(), [x, S] = n("all"), [C, w] = n("priorityScore"), { data: T, loading: E, error: D } = d(v, {
|
|
44
44
|
variables: {
|
|
45
|
-
workspaceId:
|
|
46
|
-
filter:
|
|
45
|
+
workspaceId: b,
|
|
46
|
+
filter: x === "all" ? void 0 : { status: x },
|
|
47
47
|
pagination: { first: 50 }
|
|
48
48
|
},
|
|
49
|
-
skip: !
|
|
49
|
+
skip: !b
|
|
50
50
|
});
|
|
51
|
-
if (
|
|
51
|
+
if (E) return /* @__PURE__ */ f("div", {
|
|
52
52
|
className: "flex items-center justify-center p-12",
|
|
53
|
-
children: /* @__PURE__ */
|
|
53
|
+
children: /* @__PURE__ */ f(a, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
54
54
|
});
|
|
55
|
-
if (
|
|
55
|
+
if (D) return /* @__PURE__ */ p("div", {
|
|
56
56
|
className: "p-6 text-destructive",
|
|
57
|
-
children: ["Failed to load features: ",
|
|
57
|
+
children: ["Failed to load features: ", D.message]
|
|
58
58
|
});
|
|
59
|
-
let
|
|
60
|
-
let n = e[
|
|
61
|
-
return (t[
|
|
62
|
-
}),
|
|
59
|
+
let O = T?.planmagnetFeatureRequests?.items ?? [], k = T?.planmagnetFeatureRequests?.totalCount ?? 0, A = [...O].sort((e, t) => {
|
|
60
|
+
let n = e[C] ?? 0;
|
|
61
|
+
return (t[C] ?? 0) - n;
|
|
62
|
+
}), j = [
|
|
63
63
|
"all",
|
|
64
64
|
"SUBMITTED",
|
|
65
65
|
"UNDER_REVIEW",
|
|
@@ -68,173 +68,174 @@ function y() {
|
|
|
68
68
|
"COMPLETED",
|
|
69
69
|
"DECLINED",
|
|
70
70
|
"DUPLICATE"
|
|
71
|
-
],
|
|
72
|
-
total:
|
|
73
|
-
inProgress:
|
|
74
|
-
completed:
|
|
75
|
-
avgPriority:
|
|
71
|
+
], M = {
|
|
72
|
+
total: k,
|
|
73
|
+
inProgress: O.filter((e) => e.status === "IN_PROGRESS").length,
|
|
74
|
+
completed: O.filter((e) => e.status === "COMPLETED").length,
|
|
75
|
+
avgPriority: O.length > 0 ? Math.round(O.reduce((e, t) => e + (t.priorityScore ?? 0), 0) / O.length) : 0
|
|
76
76
|
};
|
|
77
|
-
return /* @__PURE__ */
|
|
77
|
+
return /* @__PURE__ */ p("div", {
|
|
78
78
|
className: "space-y-6 p-6",
|
|
79
79
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ p("div", {
|
|
81
81
|
className: "flex items-center justify-between",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
82
|
+
children: [/* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
|
|
83
83
|
className: "text-2xl font-semibold",
|
|
84
84
|
children: "Features"
|
|
85
|
-
}), /* @__PURE__ */
|
|
85
|
+
}), /* @__PURE__ */ p("p", {
|
|
86
86
|
className: "text-sm text-muted-foreground",
|
|
87
|
-
children: [
|
|
88
|
-
})] }), /* @__PURE__ */
|
|
87
|
+
children: [k, " feature requests across all products"]
|
|
88
|
+
})] }), /* @__PURE__ */ p(h, {
|
|
89
89
|
size: "sm",
|
|
90
|
-
|
|
90
|
+
onClick: () => u("/products/new-feature-page"),
|
|
91
|
+
children: [/* @__PURE__ */ f(o, { className: "mr-1 h-4 w-4" }), "New Feature"]
|
|
91
92
|
})]
|
|
92
93
|
}),
|
|
93
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ p("div", {
|
|
94
95
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
95
96
|
children: [
|
|
96
|
-
/* @__PURE__ */
|
|
97
|
+
/* @__PURE__ */ f(g, { children: /* @__PURE__ */ p(_, {
|
|
97
98
|
className: "pt-4",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
99
|
+
children: [/* @__PURE__ */ f("p", {
|
|
99
100
|
className: "text-xs text-muted-foreground",
|
|
100
101
|
children: "Total Features"
|
|
101
|
-
}), /* @__PURE__ */
|
|
102
|
+
}), /* @__PURE__ */ f("p", {
|
|
102
103
|
className: "text-xl font-bold",
|
|
103
|
-
children:
|
|
104
|
+
children: M.total
|
|
104
105
|
})]
|
|
105
106
|
}) }),
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ f(g, { children: /* @__PURE__ */ p(_, {
|
|
107
108
|
className: "pt-4",
|
|
108
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ f("p", {
|
|
109
110
|
className: "text-xs text-muted-foreground",
|
|
110
111
|
children: "In Progress"
|
|
111
|
-
}), /* @__PURE__ */
|
|
112
|
+
}), /* @__PURE__ */ f("p", {
|
|
112
113
|
className: "text-xl font-bold",
|
|
113
|
-
children:
|
|
114
|
+
children: M.inProgress
|
|
114
115
|
})]
|
|
115
116
|
}) }),
|
|
116
|
-
/* @__PURE__ */
|
|
117
|
+
/* @__PURE__ */ f(g, { children: /* @__PURE__ */ p(_, {
|
|
117
118
|
className: "pt-4",
|
|
118
|
-
children: [/* @__PURE__ */
|
|
119
|
+
children: [/* @__PURE__ */ f("p", {
|
|
119
120
|
className: "text-xs text-muted-foreground",
|
|
120
121
|
children: "Completed"
|
|
121
|
-
}), /* @__PURE__ */
|
|
122
|
+
}), /* @__PURE__ */ f("p", {
|
|
122
123
|
className: "text-xl font-bold",
|
|
123
|
-
children:
|
|
124
|
+
children: M.completed
|
|
124
125
|
})]
|
|
125
126
|
}) }),
|
|
126
|
-
/* @__PURE__ */
|
|
127
|
+
/* @__PURE__ */ f(g, { children: /* @__PURE__ */ p(_, {
|
|
127
128
|
className: "pt-4",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
129
|
+
children: [/* @__PURE__ */ f("p", {
|
|
129
130
|
className: "text-xs text-muted-foreground",
|
|
130
131
|
children: "Avg Priority Score"
|
|
131
|
-
}), /* @__PURE__ */
|
|
132
|
+
}), /* @__PURE__ */ f("p", {
|
|
132
133
|
className: "text-xl font-bold",
|
|
133
|
-
children:
|
|
134
|
+
children: M.avgPriority
|
|
134
135
|
})]
|
|
135
136
|
}) })
|
|
136
137
|
]
|
|
137
138
|
}),
|
|
138
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ p("div", {
|
|
139
140
|
className: "flex items-center justify-between flex-wrap gap-2",
|
|
140
|
-
children: [/* @__PURE__ */
|
|
141
|
+
children: [/* @__PURE__ */ p("div", {
|
|
141
142
|
className: "flex items-center gap-2",
|
|
142
|
-
children: [/* @__PURE__ */
|
|
143
|
-
onClick: () =>
|
|
144
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
143
|
+
children: [/* @__PURE__ */ f(i, { className: "h-4 w-4 text-muted-foreground" }), j.map((e) => /* @__PURE__ */ f("button", {
|
|
144
|
+
onClick: () => S(e),
|
|
145
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${x === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
145
146
|
children: e === "all" ? "All" : e.toLowerCase().replace("_", " ")
|
|
146
147
|
}, e))]
|
|
147
|
-
}), /* @__PURE__ */
|
|
148
|
+
}), /* @__PURE__ */ p("div", {
|
|
148
149
|
className: "flex items-center gap-2",
|
|
149
|
-
children: [/* @__PURE__ */
|
|
150
|
+
children: [/* @__PURE__ */ f(r, { className: "h-4 w-4 text-muted-foreground" }), [
|
|
150
151
|
"priorityScore",
|
|
151
152
|
"voteCount",
|
|
152
153
|
"businessValue"
|
|
153
|
-
].map((e) => /* @__PURE__ */
|
|
154
|
-
onClick: () =>
|
|
155
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
154
|
+
].map((e) => /* @__PURE__ */ f("button", {
|
|
155
|
+
onClick: () => w(e),
|
|
156
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${C === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
156
157
|
children: e === "priorityScore" ? "Priority" : e === "voteCount" ? "Votes" : "Value"
|
|
157
158
|
}, e))]
|
|
158
159
|
})]
|
|
159
160
|
}),
|
|
160
|
-
/* @__PURE__ */
|
|
161
|
+
/* @__PURE__ */ f("div", {
|
|
161
162
|
className: "space-y-3",
|
|
162
|
-
children:
|
|
163
|
+
children: A.map((e) => /* @__PURE__ */ f(g, { children: /* @__PURE__ */ f(_, {
|
|
163
164
|
className: "py-4",
|
|
164
|
-
children: /* @__PURE__ */
|
|
165
|
+
children: /* @__PURE__ */ p("div", {
|
|
165
166
|
className: "flex items-start justify-between gap-4",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ p("div", {
|
|
167
168
|
className: "flex-1 space-y-2",
|
|
168
169
|
children: [
|
|
169
|
-
/* @__PURE__ */
|
|
170
|
+
/* @__PURE__ */ p("div", {
|
|
170
171
|
className: "flex items-center gap-2",
|
|
171
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ f("span", {
|
|
172
173
|
className: "text-sm font-medium",
|
|
173
174
|
children: e.title
|
|
174
|
-
}), /* @__PURE__ */
|
|
175
|
-
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${
|
|
175
|
+
}), /* @__PURE__ */ f("span", {
|
|
176
|
+
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${y[e.status] ?? "bg-gray-100"}`,
|
|
176
177
|
children: e.status.toLowerCase().replace("_", " ")
|
|
177
178
|
})]
|
|
178
179
|
}),
|
|
179
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ f("p", {
|
|
180
181
|
className: "text-xs text-muted-foreground",
|
|
181
182
|
children: e.description
|
|
182
183
|
}),
|
|
183
|
-
/* @__PURE__ */
|
|
184
|
+
/* @__PURE__ */ p("div", {
|
|
184
185
|
className: "flex items-center gap-4 text-xs text-muted-foreground",
|
|
185
186
|
children: [
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
-
/* @__PURE__ */
|
|
188
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ p("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
|
|
188
|
+
/* @__PURE__ */ p("span", { children: ["Submitted by: ", e.submittedBy] }),
|
|
189
|
+
/* @__PURE__ */ f("span", { children: new Date(e.updatedAt).toLocaleDateString() })
|
|
189
190
|
]
|
|
190
191
|
}),
|
|
191
|
-
e.tags.length > 0 && /* @__PURE__ */
|
|
192
|
+
e.tags.length > 0 && /* @__PURE__ */ f("div", {
|
|
192
193
|
className: "flex gap-1",
|
|
193
|
-
children: e.tags.map((e) => /* @__PURE__ */
|
|
194
|
+
children: e.tags.map((e) => /* @__PURE__ */ f(m, {
|
|
194
195
|
variant: "outline",
|
|
195
196
|
className: "text-[10px]",
|
|
196
197
|
children: e
|
|
197
198
|
}, e))
|
|
198
199
|
})
|
|
199
200
|
]
|
|
200
|
-
}), /* @__PURE__ */
|
|
201
|
+
}), /* @__PURE__ */ p("div", {
|
|
201
202
|
className: "flex items-center gap-4 shrink-0",
|
|
202
203
|
children: [
|
|
203
|
-
/* @__PURE__ */
|
|
204
|
+
/* @__PURE__ */ p("div", {
|
|
204
205
|
className: "text-center",
|
|
205
|
-
children: [/* @__PURE__ */
|
|
206
|
+
children: [/* @__PURE__ */ p("div", {
|
|
206
207
|
className: "flex items-center gap-1",
|
|
207
|
-
children: [/* @__PURE__ */
|
|
208
|
+
children: [/* @__PURE__ */ f(c, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ f("span", {
|
|
208
209
|
className: "text-sm font-bold",
|
|
209
210
|
children: e.priorityScore ?? 0
|
|
210
211
|
})]
|
|
211
|
-
}), /* @__PURE__ */
|
|
212
|
+
}), /* @__PURE__ */ f("p", {
|
|
212
213
|
className: "text-[10px] text-muted-foreground",
|
|
213
214
|
children: "Priority"
|
|
214
215
|
})]
|
|
215
216
|
}),
|
|
216
|
-
/* @__PURE__ */
|
|
217
|
+
/* @__PURE__ */ p("div", {
|
|
217
218
|
className: "text-center",
|
|
218
|
-
children: [/* @__PURE__ */
|
|
219
|
+
children: [/* @__PURE__ */ p("div", {
|
|
219
220
|
className: "flex items-center gap-1",
|
|
220
|
-
children: [/* @__PURE__ */
|
|
221
|
+
children: [/* @__PURE__ */ f(l, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ f("span", {
|
|
221
222
|
className: "text-sm font-bold",
|
|
222
223
|
children: e.voteCount
|
|
223
224
|
})]
|
|
224
|
-
}), /* @__PURE__ */
|
|
225
|
+
}), /* @__PURE__ */ f("p", {
|
|
225
226
|
className: "text-[10px] text-muted-foreground",
|
|
226
227
|
children: "Votes"
|
|
227
228
|
})]
|
|
228
229
|
}),
|
|
229
|
-
/* @__PURE__ */
|
|
230
|
+
/* @__PURE__ */ p("div", {
|
|
230
231
|
className: "text-center",
|
|
231
|
-
children: [/* @__PURE__ */
|
|
232
|
+
children: [/* @__PURE__ */ p("div", {
|
|
232
233
|
className: "flex items-center gap-1",
|
|
233
|
-
children: [/* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ f(s, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ f("span", {
|
|
234
235
|
className: "text-sm font-bold",
|
|
235
236
|
children: e.businessValue ?? 0
|
|
236
237
|
})]
|
|
237
|
-
}), /* @__PURE__ */
|
|
238
|
+
}), /* @__PURE__ */ f("p", {
|
|
238
239
|
className: "text-[10px] text-muted-foreground",
|
|
239
240
|
children: "Value"
|
|
240
241
|
})]
|
|
@@ -244,7 +245,7 @@ function y() {
|
|
|
244
245
|
})
|
|
245
246
|
}) }, e.id))
|
|
246
247
|
}),
|
|
247
|
-
|
|
248
|
+
A.length === 0 && /* @__PURE__ */ f("p", {
|
|
248
249
|
className: "text-center text-muted-foreground py-8",
|
|
249
250
|
children: "No features match the selected filters."
|
|
250
251
|
})
|
|
@@ -252,6 +253,6 @@ function y() {
|
|
|
252
253
|
});
|
|
253
254
|
}
|
|
254
255
|
//#endregion
|
|
255
|
-
export {
|
|
256
|
+
export { b as ProductsFeaturesPagePage };
|
|
256
257
|
|
|
257
258
|
//# sourceMappingURL=ProductsFeaturesPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: PlanmagnetFeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n const [sortBy, setSortBy] = useState<'priorityScore' | 'voteCount' | 'businessValue'>(\n 'priorityScore'\n );\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n filter: statusFilter !== 'all' ? { status: statusFilter } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\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 features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = [...features].sort((a, b) => {\n const aVal = a[sortBy] ?? 0;\n const bVal = b[sortBy] ?? 0;\n return (bVal as number) - (aVal as number);\n });\n\n const statuses = [\n 'all',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n const stats = {\n total: totalCount,\n inProgress: features.filter((f) => f.status === 'IN_PROGRESS').length,\n completed: features.filter((f) => f.status === 'COMPLETED').length,\n avgPriority:\n features.length > 0\n ? Math.round(features.reduce((s, f) => s + (f.priorityScore ?? 0), 0) / features.length)\n : 0,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} feature requests across all products\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Avg Priority Score</p>\n <p className=\"text-xl font-bold\">{stats.avgPriority}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all' ? 'All' : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['priorityScore', 'voteCount', 'businessValue'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'priorityScore' ? 'Priority' : s === 'voteCount' ? 'Votes' : 'Value'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card key={feature.id}>\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n <span>Submitted by: {feature.submittedBy}</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No features match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: PlanmagnetFeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n const [sortBy, setSortBy] = useState<'priorityScore' | 'voteCount' | 'businessValue'>(\n 'priorityScore'\n );\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n filter: statusFilter !== 'all' ? { status: statusFilter } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\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 features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = [...features].sort((a, b) => {\n const aVal = a[sortBy] ?? 0;\n const bVal = b[sortBy] ?? 0;\n return (bVal as number) - (aVal as number);\n });\n\n const statuses = [\n 'all',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n const stats = {\n total: totalCount,\n inProgress: features.filter((f) => f.status === 'IN_PROGRESS').length,\n completed: features.filter((f) => f.status === 'COMPLETED').length,\n avgPriority:\n features.length > 0\n ? Math.round(features.reduce((s, f) => s + (f.priorityScore ?? 0), 0) / features.length)\n : 0,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} feature requests across all products\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/new-feature-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Avg Priority Score</p>\n <p className=\"text-xl font-bold\">{stats.avgPriority}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all' ? 'All' : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['priorityScore', 'voteCount', 'businessValue'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'priorityScore' ? 'Priority' : s === 'voteCount' ? 'Votes' : 'Value'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card key={feature.id}>\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n <span>Submitted by: {feature.submittedBy}</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No features match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAkBA,IAAM,IAAwB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;GAyC3B,IAAsC;CAC1C,WAAW;CACX,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,UAAU;CACV,WAAW;CACZ;AAED,SAAgB,IAA2B;CACzC,IAAM,IAAW,GAAuB,EAClC,EAAE,mBAAgB,GAAe,EACjC,CAAC,GAAc,KAAmB,EAAiB,MAAM,EACzD,CAAC,GAAQ,KAAa,EAC1B,gBACD,EAEK,EACJ,SACA,YACA,aAGA,EAA8B,GAAuB;EACnD,WAAW;GACT;GACA,QAAQ,MAAiB,QAAmC,KAAA,IAA3B,EAAE,QAAQ,GAAc;GACzD,YAAY,EAAE,OAAO,IAAI;GAC1B;EACD,MAAM,CAAC;EACR,CAAC;AAEJ,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,6BAA0B,EAAM,QAAc;;CAG7F,IAAM,IAAiC,GAAM,2BAA2B,SAAS,EAAE,EAC7E,IAAa,GAAM,2BAA2B,cAAc,GAE5D,IAAS,CAAC,GAAG,EAAS,CAAC,MAAM,GAAG,MAAM;EAC1C,IAAM,IAAO,EAAE,MAAW;AAE1B,UADa,EAAE,MAAW,KACC;GAC3B,EAEI,IAAW;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAEK,IAAQ;EACZ,OAAO;EACP,YAAY,EAAS,QAAQ,MAAM,EAAE,WAAW,cAAc,CAAC;EAC/D,WAAW,EAAS,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;EAC5D,aACE,EAAS,SAAS,IACd,KAAK,MAAM,EAAS,QAAQ,GAAG,MAAM,KAAK,EAAE,iBAAiB,IAAI,EAAE,GAAG,EAAS,OAAO,GACtF;EACP;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAa,CAAA,EACpD,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,GAAW,wCACV;OACA,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAS,6BAA6B;eAAvE,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,cAE1B;OACL;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAkB,CAAA,EAC/D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAU,CAAA,CACtC;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAe,CAAA,EAC5D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAe,CAAA,CAC3C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAa,CAAA,EAC1D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAc,CAAA,CAC1C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAsB,CAAA,EACnE,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAgB,CAAA,CAC5C;SACT,CAAA;KACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,iCAAkC,CAAA,EACnD,EAAS,KAAK,MACb,kBAAC,UAAD;MAEE,eAAe,EAAgB,EAAE;MACjC,WAAW,2CAA2C,MAAiB,IAAI,uCAAuC;gBAEjH,MAAM,QAAQ,QAAQ,EAAE,aAAa,CAAC,QAAQ,KAAK,IAAI;MACjD,EALF,EAKE,CACT,CACE;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAa,WAAU,iCAAkC,CAAA,EACvD;MAAC;MAAiB;MAAa;MAAgB,CAAW,KAAK,MAC/D,kBAAC,UAAD;MAEE,eAAe,EAAU,EAAE;MAC3B,WAAW,2CAA2C,MAAW,IAAI,uCAAuC;gBAE3G,MAAM,kBAAkB,aAAa,MAAM,cAAc,UAAU;MAC7D,EALF,EAKE,CACT,CACE;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MACX,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eACrB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAuB,EAAQ;UAAa,CAAA,EAC5D,kBAAC,QAAD;UACE,WAAW,qDAAqD,EAAY,EAAQ,WAAW;oBAE9F,EAAQ,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;UAC1C,CAAA,CACH;;QACN,kBAAC,KAAD;SAAG,WAAU;mBAAiC,EAAQ;SAAgB,CAAA;QACtE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,QAAD,EAAA,UAAA,CAAM,YAAS,EAAQ,kBAAkB,MAAa,EAAA,CAAA;UACtD,kBAAC,QAAD,EAAA,UAAA,CAAM,kBAAe,EAAQ,YAAmB,EAAA,CAAA;UAChD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA;UAC3D;;QACL,EAAQ,KAAK,SAAS,KACrB,kBAAC,OAAD;SAAK,WAAU;mBACZ,EAAQ,KAAK,KAAK,MACjB,kBAAC,GAAD;UAAiB,SAAQ;UAAU,WAAU;oBAC1C;UACK,EAFI,EAEJ,CACR;SACE,CAAA;QAEJ;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC3C,kBAAC,QAAD;WAAM,WAAU;qBAAqB,EAAQ,iBAAiB;WAAS,CAAA,CACnE;aACN,kBAAC,KAAD;UAAG,WAAU;oBAAoC;UAAY,CAAA,CACzD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,0BAA2B,CAAA,EAC/C,kBAAC,QAAD;WAAM,WAAU;qBAAqB,EAAQ;WAAiB,CAAA,CAC1D;aACN,kBAAC,KAAD;UAAG,WAAU;oBAAoC;UAAS,CAAA,CACtD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,2BAA4B,CAAA,EAC5C,kBAAC,QAAD;WAAM,WAAU;qBAAqB,EAAQ,iBAAiB;WAAS,CAAA,CACnE;aACN,kBAAC,KAAD;UAAG,WAAU;oBAAoC;UAAS,CAAA,CACtD;;QACF;SACF;;KACM,CAAA,EACT,EArDI,EAAQ,GAqDZ,CACP;IACE,CAAA;GACL,EAAO,WAAW,KACjB,kBAAC,KAAD;IAAG,WAAU;cAAyC;IAElD,CAAA;GAEF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsRoadmapsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapsPagePage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsRoadmapsPagePage.tsx"],"names":[],"mappings":"AAkEA,wBAAgB,wBAAwB,4CAkKvC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Calendar as
|
|
4
|
-
import { gql as
|
|
5
|
-
import { useQuery as
|
|
6
|
-
import { jsx as
|
|
7
|
-
import { Badge as
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { useState as n } from "react";
|
|
3
|
+
import { Calendar as r, Filter as i, Loader2 as a, Map as o, Plus as s } from "lucide-react";
|
|
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 { Badge as f, Button as p, Card as m, CardContent as h, CardHeader as g, CardTitle as _ } from "@burdenoff/fe-libs/ui";
|
|
8
8
|
//#region src/planmagnet/pages/ProductsRoadmapsPagePage.tsx
|
|
9
|
-
var
|
|
9
|
+
var v = c`
|
|
10
10
|
query ListRoadmapsPage(
|
|
11
11
|
$workspaceId: ID!
|
|
12
12
|
$filter: PlanmagnetRoadmapFilter
|
|
@@ -34,149 +34,150 @@ var _ = s`
|
|
|
34
34
|
totalCount
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
|
-
`,
|
|
37
|
+
`, y = {
|
|
38
38
|
TIMELINE: "bg-purple-100 text-purple-800",
|
|
39
39
|
GANTT: "bg-orange-100 text-orange-800",
|
|
40
40
|
SWIMLANE: "bg-green-100 text-green-800",
|
|
41
41
|
CUSTOM: "bg-blue-100 text-blue-800"
|
|
42
42
|
};
|
|
43
|
-
function
|
|
44
|
-
let { workspaceId:
|
|
43
|
+
function b() {
|
|
44
|
+
let c = t(), { workspaceId: b } = e(), [x, S] = n("all"), [C, w] = n("all"), { data: T, loading: E, error: D } = l(v, {
|
|
45
45
|
variables: {
|
|
46
|
-
workspaceId:
|
|
46
|
+
workspaceId: b,
|
|
47
47
|
pagination: { first: 50 }
|
|
48
48
|
},
|
|
49
|
-
skip: !
|
|
49
|
+
skip: !b
|
|
50
50
|
});
|
|
51
|
-
if (
|
|
51
|
+
if (E) return /* @__PURE__ */ u("div", {
|
|
52
52
|
className: "flex items-center justify-center p-12",
|
|
53
|
-
children: /* @__PURE__ */
|
|
53
|
+
children: /* @__PURE__ */ u(a, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
54
54
|
});
|
|
55
|
-
if (
|
|
55
|
+
if (D) return /* @__PURE__ */ d("div", {
|
|
56
56
|
className: "p-6 text-destructive",
|
|
57
|
-
children: ["Failed to load roadmaps: ",
|
|
57
|
+
children: ["Failed to load roadmaps: ", D.message]
|
|
58
58
|
});
|
|
59
|
-
let
|
|
59
|
+
let O = T?.planmagnetRoadmaps?.items ?? [], k = T?.planmagnetRoadmaps?.totalCount ?? 0, A = ["all", ...new Set(O.map((e) => e.type))], j = [
|
|
60
60
|
"all",
|
|
61
61
|
"public",
|
|
62
62
|
"private"
|
|
63
|
-
],
|
|
64
|
-
return /* @__PURE__ */
|
|
63
|
+
], M = O.filter((e) => (x === "all" || e.type === x) && (C === "all" || C === "public" && e.isPublic || C === "private" && !e.isPublic));
|
|
64
|
+
return /* @__PURE__ */ d("div", {
|
|
65
65
|
className: "space-y-6 p-6",
|
|
66
66
|
children: [
|
|
67
|
-
/* @__PURE__ */
|
|
67
|
+
/* @__PURE__ */ d("div", {
|
|
68
68
|
className: "flex items-center justify-between",
|
|
69
|
-
children: [/* @__PURE__ */
|
|
69
|
+
children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
|
|
70
70
|
className: "text-2xl font-semibold",
|
|
71
71
|
children: "Roadmaps"
|
|
72
|
-
}), /* @__PURE__ */
|
|
72
|
+
}), /* @__PURE__ */ d("p", {
|
|
73
73
|
className: "text-sm text-muted-foreground",
|
|
74
|
-
children: [
|
|
75
|
-
})] }), /* @__PURE__ */
|
|
74
|
+
children: [k, " roadmaps across all products"]
|
|
75
|
+
})] }), /* @__PURE__ */ d(p, {
|
|
76
76
|
size: "sm",
|
|
77
|
-
|
|
77
|
+
onClick: () => c("/products/create-roadmap-page"),
|
|
78
|
+
children: [/* @__PURE__ */ u(s, { className: "mr-1 h-4 w-4" }), "New Roadmap"]
|
|
78
79
|
})]
|
|
79
80
|
}),
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ d("div", {
|
|
81
82
|
className: "flex gap-4 flex-wrap",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ d("div", {
|
|
83
84
|
className: "flex items-center gap-2",
|
|
84
85
|
children: [
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
-
/* @__PURE__ */
|
|
86
|
+
/* @__PURE__ */ u(i, { className: "h-4 w-4 text-muted-foreground" }),
|
|
87
|
+
/* @__PURE__ */ u("span", {
|
|
87
88
|
className: "text-sm text-muted-foreground",
|
|
88
89
|
children: "Type:"
|
|
89
90
|
}),
|
|
90
|
-
|
|
91
|
-
onClick: () =>
|
|
92
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
91
|
+
A.map((e) => /* @__PURE__ */ u("button", {
|
|
92
|
+
onClick: () => S(e),
|
|
93
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${x === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
93
94
|
children: e === "all" ? "All" : e.toLowerCase()
|
|
94
95
|
}, e))
|
|
95
96
|
]
|
|
96
|
-
}), /* @__PURE__ */
|
|
97
|
+
}), /* @__PURE__ */ d("div", {
|
|
97
98
|
className: "flex items-center gap-2",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
99
|
+
children: [/* @__PURE__ */ u("span", {
|
|
99
100
|
className: "text-sm text-muted-foreground",
|
|
100
101
|
children: "Visibility:"
|
|
101
|
-
}),
|
|
102
|
-
onClick: () =>
|
|
103
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
102
|
+
}), j.map((e) => /* @__PURE__ */ u("button", {
|
|
103
|
+
onClick: () => w(e),
|
|
104
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${C === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
104
105
|
children: e
|
|
105
106
|
}, e))]
|
|
106
107
|
})]
|
|
107
108
|
}),
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ u("div", {
|
|
109
110
|
className: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
|
110
|
-
children:
|
|
111
|
-
let t = e.items?.length ?? 0,
|
|
112
|
-
return /* @__PURE__ */
|
|
111
|
+
children: M.map((e) => {
|
|
112
|
+
let t = e.items?.length ?? 0, n = e.items?.filter((e) => e.status === "COMPLETED" || e.status === "DONE").length ?? 0, i = t > 0 ? Math.round(n / t * 100) : 0, a = e.items?.map((e) => e.startDate).filter(Boolean) ?? [], s = e.items?.map((e) => e.endDate).filter(Boolean) ?? [], c = a.length > 0 ? a.sort()[0] : null, l = s.length > 0 ? s.sort().reverse()[0] : null;
|
|
113
|
+
return /* @__PURE__ */ d(m, { children: [/* @__PURE__ */ u(g, {
|
|
113
114
|
className: "pb-2",
|
|
114
|
-
children: /* @__PURE__ */
|
|
115
|
+
children: /* @__PURE__ */ d("div", {
|
|
115
116
|
className: "flex items-center justify-between",
|
|
116
|
-
children: [/* @__PURE__ */
|
|
117
|
+
children: [/* @__PURE__ */ u(_, {
|
|
117
118
|
className: "text-base",
|
|
118
119
|
children: e.name
|
|
119
|
-
}), /* @__PURE__ */
|
|
120
|
+
}), /* @__PURE__ */ u(f, {
|
|
120
121
|
variant: e.isPublic ? "default" : "secondary",
|
|
121
122
|
className: "capitalize",
|
|
122
123
|
children: e.isPublic ? "public" : "private"
|
|
123
124
|
})]
|
|
124
125
|
})
|
|
125
|
-
}), /* @__PURE__ */
|
|
126
|
+
}), /* @__PURE__ */ d(h, {
|
|
126
127
|
className: "space-y-3",
|
|
127
128
|
children: [
|
|
128
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ u("p", {
|
|
129
130
|
className: "text-sm text-muted-foreground",
|
|
130
131
|
children: e.description ?? "No description"
|
|
131
132
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
133
|
+
/* @__PURE__ */ d("div", {
|
|
133
134
|
className: "flex items-center gap-2",
|
|
134
|
-
children: [/* @__PURE__ */
|
|
135
|
-
className: `text-xs px-2 py-0.5 rounded-full capitalize ${
|
|
135
|
+
children: [/* @__PURE__ */ u("span", {
|
|
136
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${y[e.type] ?? "bg-gray-100"}`,
|
|
136
137
|
children: e.type.toLowerCase()
|
|
137
|
-
}), /* @__PURE__ */
|
|
138
|
+
}), /* @__PURE__ */ u(f, {
|
|
138
139
|
variant: "outline",
|
|
139
140
|
className: "text-xs capitalize",
|
|
140
141
|
children: e.isPublic ? "public" : "private"
|
|
141
142
|
})]
|
|
142
143
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
|
|
144
145
|
className: "flex justify-between text-xs mb-1",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
+
children: [/* @__PURE__ */ u("span", {
|
|
146
147
|
className: "text-muted-foreground",
|
|
147
148
|
children: "Progress"
|
|
148
|
-
}), /* @__PURE__ */
|
|
149
|
+
}), /* @__PURE__ */ d("span", {
|
|
149
150
|
className: "font-medium",
|
|
150
151
|
children: [i, "%"]
|
|
151
152
|
})]
|
|
152
|
-
}), /* @__PURE__ */
|
|
153
|
+
}), /* @__PURE__ */ u("div", {
|
|
153
154
|
className: "w-full h-2 rounded-full bg-muted overflow-hidden",
|
|
154
|
-
children: /* @__PURE__ */
|
|
155
|
+
children: /* @__PURE__ */ u("div", {
|
|
155
156
|
className: "h-full bg-primary rounded-full",
|
|
156
157
|
style: { width: `${i}%` }
|
|
157
158
|
})
|
|
158
159
|
})] }),
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ u("div", {
|
|
160
161
|
className: "flex gap-4 text-xs text-muted-foreground",
|
|
161
|
-
children: /* @__PURE__ */
|
|
162
|
+
children: /* @__PURE__ */ d("span", {
|
|
162
163
|
className: "flex items-center gap-1",
|
|
163
164
|
children: [
|
|
164
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ u(o, { className: "h-3 w-3" }),
|
|
165
166
|
t,
|
|
166
167
|
" items"
|
|
167
168
|
]
|
|
168
169
|
})
|
|
169
170
|
}),
|
|
170
|
-
/* @__PURE__ */
|
|
171
|
+
/* @__PURE__ */ d("div", {
|
|
171
172
|
className: "flex items-center justify-between text-xs text-muted-foreground border-t pt-2",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
173
|
+
children: [/* @__PURE__ */ d("span", { children: ["Owner: ", e.ownerId] }), c && l && /* @__PURE__ */ d("span", {
|
|
173
174
|
className: "flex items-center gap-1",
|
|
174
175
|
children: [
|
|
175
|
-
/* @__PURE__ */
|
|
176
|
+
/* @__PURE__ */ u(r, { className: "h-3 w-3" }),
|
|
176
177
|
new Date(c).toLocaleDateString(),
|
|
177
178
|
" -",
|
|
178
179
|
" ",
|
|
179
|
-
new Date(
|
|
180
|
+
new Date(l).toLocaleDateString()
|
|
180
181
|
]
|
|
181
182
|
})]
|
|
182
183
|
})
|
|
@@ -184,7 +185,7 @@ function y() {
|
|
|
184
185
|
})] }, e.id);
|
|
185
186
|
})
|
|
186
187
|
}),
|
|
187
|
-
|
|
188
|
+
M.length === 0 && /* @__PURE__ */ u("p", {
|
|
188
189
|
className: "text-center text-muted-foreground py-8",
|
|
189
190
|
children: "No roadmaps match the selected filters."
|
|
190
191
|
})
|
|
@@ -192,6 +193,6 @@ function y() {
|
|
|
192
193
|
});
|
|
193
194
|
}
|
|
194
195
|
//#endregion
|
|
195
|
-
export {
|
|
196
|
+
export { b as ProductsRoadmapsPagePage };
|
|
196
197
|
|
|
197
198
|
//# sourceMappingURL=ProductsRoadmapsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsRoadmapsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Map, Calendar, Plus, Filter, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS = gql`\n query ListRoadmapsPage(\n $workspaceId: ID!\n $filter: PlanmagnetRoadmapFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetRoadmaps(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n workspaceId\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 }\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}\n\ninterface RoadmapNode {\n id: string;\n name: string;\n description: string | null;\n type: string;\n isPublic: boolean;\n ownerId: string;\n createdAt: string;\n updatedAt: string;\n items: RoadmapItemNode[];\n}\n\nconst typeColor: Record<string, string> = {\n TIMELINE: 'bg-purple-100 text-purple-800',\n GANTT: 'bg-orange-100 text-orange-800',\n SWIMLANE: 'bg-green-100 text-green-800',\n CUSTOM: 'bg-blue-100 text-blue-800',\n};\n\nexport function ProductsRoadmapsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const [typeFilter, setTypeFilter] = useState<string>('all');\n const [visFilter, setVisFilter] = useState<string>('all');\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>>(LIST_ROADMAPS, {\n variables: {\n workspaceId,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\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 roadmaps: {error.message}</div>;\n }\n\n const roadmaps: RoadmapNode[] = data?.planmagnetRoadmaps?.items ?? [];\n const totalCount = data?.planmagnetRoadmaps?.totalCount ?? 0;\n\n const types = ['all', ...new Set(roadmaps.map((r) => r.type))];\n const visibilities = ['all', 'public', 'private'];\n\n const filtered = roadmaps.filter(\n (r) =>\n (typeFilter === 'all' || r.type === typeFilter) &&\n (visFilter === 'all' ||\n (visFilter === 'public' && r.isPublic) ||\n (visFilter === 'private' && !r.isPublic))\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Roadmaps</h1>\n <p className=\"text-sm text-muted-foreground\">{totalCount} roadmaps across all products</p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New Roadmap\n </Button>\n </div>\n\n <div className=\"flex gap-4 flex-wrap\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">Type:</span>\n {types.map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t === 'all' ? 'All' : t.toLowerCase()}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">Visibility:</span>\n {visibilities.map((v) => (\n <button\n key={v}\n onClick={() => setVisFilter(v)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${visFilter === v ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {filtered.map((rm) => {\n const itemCount = rm.items?.length ?? 0;\n const completedItems =\n rm.items?.filter((i) => i.status === 'COMPLETED' || i.status === 'DONE').length ?? 0;\n const progress = itemCount > 0 ? Math.round((completedItems / itemCount) * 100) : 0;\n const startDates = rm.items?.map((i) => i.startDate).filter(Boolean) ?? [];\n const endDates = rm.items?.map((i) => i.endDate).filter(Boolean) ?? [];\n const earliestStart = startDates.length > 0 ? startDates.sort()[0] : null;\n const latestEnd = endDates.length > 0 ? endDates.sort().reverse()[0] : null;\n\n return (\n <Card key={rm.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{rm.name}</CardTitle>\n <Badge variant={rm.isPublic ? 'default' : 'secondary'} className=\"capitalize\">\n {rm.isPublic ? 'public' : 'private'}\n </Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {rm.description ?? 'No description'}\n </p>\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${typeColor[rm.type] ?? 'bg-gray-100'}`}\n >\n {rm.type.toLowerCase()}\n </span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {rm.isPublic ? 'public' : 'private'}\n </Badge>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Progress</span>\n <span className=\"font-medium\">{progress}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Map className=\"h-3 w-3\" />\n {itemCount} items\n </span>\n </div>\n <div className=\"flex items-center justify-between text-xs text-muted-foreground border-t pt-2\">\n <span>Owner: {rm.ownerId}</span>\n {earliestStart && latestEnd && (\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {new Date(earliestStart).toLocaleDateString()} -{' '}\n {new Date(latestEnd).toLocaleDateString()}\n </span>\n )}\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n {filtered.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No roadmaps match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProductsRoadmapsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsRoadmapsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Map, Calendar, Plus, Filter, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst LIST_ROADMAPS = gql`\n query ListRoadmapsPage(\n $workspaceId: ID!\n $filter: PlanmagnetRoadmapFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetRoadmaps(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n workspaceId\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 }\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}\n\ninterface RoadmapNode {\n id: string;\n name: string;\n description: string | null;\n type: string;\n isPublic: boolean;\n ownerId: string;\n createdAt: string;\n updatedAt: string;\n items: RoadmapItemNode[];\n}\n\nconst typeColor: Record<string, string> = {\n TIMELINE: 'bg-purple-100 text-purple-800',\n GANTT: 'bg-orange-100 text-orange-800',\n SWIMLANE: 'bg-green-100 text-green-800',\n CUSTOM: 'bg-blue-100 text-blue-800',\n};\n\nexport function ProductsRoadmapsPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [typeFilter, setTypeFilter] = useState<string>('all');\n const [visFilter, setVisFilter] = useState<string>('all');\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>>(LIST_ROADMAPS, {\n variables: {\n workspaceId,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\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 roadmaps: {error.message}</div>;\n }\n\n const roadmaps: RoadmapNode[] = data?.planmagnetRoadmaps?.items ?? [];\n const totalCount = data?.planmagnetRoadmaps?.totalCount ?? 0;\n\n const types = ['all', ...new Set(roadmaps.map((r) => r.type))];\n const visibilities = ['all', 'public', 'private'];\n\n const filtered = roadmaps.filter(\n (r) =>\n (typeFilter === 'all' || r.type === typeFilter) &&\n (visFilter === 'all' ||\n (visFilter === 'public' && r.isPublic) ||\n (visFilter === 'private' && !r.isPublic))\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Roadmaps</h1>\n <p className=\"text-sm text-muted-foreground\">{totalCount} roadmaps across all products</p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/create-roadmap-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Roadmap\n </Button>\n </div>\n\n <div className=\"flex gap-4 flex-wrap\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-sm text-muted-foreground\">Type:</span>\n {types.map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {t === 'all' ? 'All' : t.toLowerCase()}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm text-muted-foreground\">Visibility:</span>\n {visibilities.map((v) => (\n <button\n key={v}\n onClick={() => setVisFilter(v)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${visFilter === v ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {v}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {filtered.map((rm) => {\n const itemCount = rm.items?.length ?? 0;\n const completedItems =\n rm.items?.filter((i) => i.status === 'COMPLETED' || i.status === 'DONE').length ?? 0;\n const progress = itemCount > 0 ? Math.round((completedItems / itemCount) * 100) : 0;\n const startDates = rm.items?.map((i) => i.startDate).filter(Boolean) ?? [];\n const endDates = rm.items?.map((i) => i.endDate).filter(Boolean) ?? [];\n const earliestStart = startDates.length > 0 ? startDates.sort()[0] : null;\n const latestEnd = endDates.length > 0 ? endDates.sort().reverse()[0] : null;\n\n return (\n <Card key={rm.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{rm.name}</CardTitle>\n <Badge variant={rm.isPublic ? 'default' : 'secondary'} className=\"capitalize\">\n {rm.isPublic ? 'public' : 'private'}\n </Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {rm.description ?? 'No description'}\n </p>\n <div className=\"flex items-center gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${typeColor[rm.type] ?? 'bg-gray-100'}`}\n >\n {rm.type.toLowerCase()}\n </span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {rm.isPublic ? 'public' : 'private'}\n </Badge>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Progress</span>\n <span className=\"font-medium\">{progress}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${progress}%` }}\n />\n </div>\n </div>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Map className=\"h-3 w-3\" />\n {itemCount} items\n </span>\n </div>\n <div className=\"flex items-center justify-between text-xs text-muted-foreground border-t pt-2\">\n <span>Owner: {rm.ownerId}</span>\n {earliestStart && latestEnd && (\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {new Date(earliestStart).toLocaleDateString()} -{' '}\n {new Date(latestEnd).toLocaleDateString()}\n </span>\n )}\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n {filtered.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No roadmaps match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectsPagePage.tsx"],"names":[],"mappings":"AAyCA,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ProjectsProjectsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectsPagePage.tsx"],"names":[],"mappings":"AAyCA,wBAAgB,wBAAwB,4CA8JvC"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import { CheckSquare as
|
|
3
|
-
import { gql as
|
|
4
|
-
import { useQuery as
|
|
5
|
-
import { jsx as
|
|
6
|
-
import { useI18n as
|
|
7
|
-
import { Badge as
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { CheckSquare as n, FolderKanban as r, Plus as i, TrendingUp as a, Users as o } from "lucide-react";
|
|
3
|
+
import { gql as s } from "@apollo/client";
|
|
4
|
+
import { useQuery as c } from "@apollo/client/react";
|
|
5
|
+
import { jsx as l, jsxs as u } from "react/jsx-runtime";
|
|
6
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
7
|
+
import { Badge as f, Button as p, Card as m, CardContent as h, CardHeader as g, CardTitle as _ } from "@burdenoff/fe-libs/ui";
|
|
8
8
|
//#region src/planmagnet/pages/ProjectsProjectsPagePage.tsx
|
|
9
|
-
var
|
|
9
|
+
var v = s`
|
|
10
10
|
query GetProjects($workspaceId: ID!) {
|
|
11
11
|
planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {
|
|
12
12
|
items {
|
|
@@ -25,135 +25,136 @@ var _ = o`
|
|
|
25
25
|
totalCount
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
`,
|
|
28
|
+
`, y = {
|
|
29
29
|
SCRUM: "bg-blue-100 text-blue-800",
|
|
30
30
|
KANBAN: "bg-purple-100 text-purple-800",
|
|
31
31
|
WATERFALL: "bg-gray-100 text-gray-800",
|
|
32
32
|
HYBRID: "bg-teal-100 text-teal-800",
|
|
33
33
|
AGILE: "bg-indigo-100 text-indigo-800",
|
|
34
34
|
CUSTOM: "bg-amber-100 text-amber-800"
|
|
35
|
-
},
|
|
36
|
-
function
|
|
37
|
-
let { workspaceId:
|
|
38
|
-
if (
|
|
35
|
+
}, b = (e) => e === "ACTIVE" ? "default" : "secondary";
|
|
36
|
+
function x() {
|
|
37
|
+
let s = t(), { workspaceId: x } = e(), { t: S } = d(), { data: C, loading: w, error: T } = c(v, { variables: { workspaceId: x ?? "default" } });
|
|
38
|
+
if (w) return /* @__PURE__ */ l("div", {
|
|
39
39
|
className: "flex items-center justify-center p-12",
|
|
40
|
-
children: /* @__PURE__ */
|
|
40
|
+
children: /* @__PURE__ */ l("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
41
41
|
});
|
|
42
|
-
if (
|
|
42
|
+
if (T) return /* @__PURE__ */ u("div", {
|
|
43
43
|
className: "p-6 text-red-500",
|
|
44
44
|
children: [
|
|
45
|
-
|
|
45
|
+
S("planmagnet.projects.errorLoading", "Error loading projects"),
|
|
46
46
|
": ",
|
|
47
|
-
|
|
47
|
+
T.message
|
|
48
48
|
]
|
|
49
49
|
});
|
|
50
|
-
let
|
|
51
|
-
total:
|
|
52
|
-
active:
|
|
53
|
-
completed:
|
|
54
|
-
onHold:
|
|
50
|
+
let E = C?.planmagnetProjects?.items ?? [], D = {
|
|
51
|
+
total: E.length,
|
|
52
|
+
active: E.filter((e) => e.status === "ACTIVE").length,
|
|
53
|
+
completed: E.filter((e) => e.status === "COMPLETED").length,
|
|
54
|
+
onHold: E.filter((e) => e.status === "ON_HOLD").length
|
|
55
55
|
};
|
|
56
|
-
return /* @__PURE__ */
|
|
56
|
+
return /* @__PURE__ */ u("div", {
|
|
57
57
|
className: "space-y-6 p-6",
|
|
58
58
|
children: [
|
|
59
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ u("div", {
|
|
60
60
|
className: "flex items-center justify-between",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
61
|
+
children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
|
|
62
62
|
className: "text-2xl font-semibold",
|
|
63
|
-
children:
|
|
64
|
-
}), /* @__PURE__ */
|
|
63
|
+
children: S("planmagnet.projects.title", "All Projects")
|
|
64
|
+
}), /* @__PURE__ */ l("p", {
|
|
65
65
|
className: "text-sm text-muted-foreground",
|
|
66
|
-
children:
|
|
67
|
-
})] }), /* @__PURE__ */
|
|
66
|
+
children: S("planmagnet.projects.subtitle", "Manage and track all projects across teams")
|
|
67
|
+
})] }), /* @__PURE__ */ u(p, {
|
|
68
68
|
size: "sm",
|
|
69
|
-
|
|
69
|
+
onClick: () => s("/projects/create-project-page"),
|
|
70
|
+
children: [/* @__PURE__ */ l(i, { className: "mr-1 h-4 w-4" }), S("planmagnet.projects.actions.newProject", "New Project")]
|
|
70
71
|
})]
|
|
71
72
|
}),
|
|
72
|
-
/* @__PURE__ */
|
|
73
|
+
/* @__PURE__ */ l("div", {
|
|
73
74
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
74
75
|
children: [
|
|
75
76
|
{
|
|
76
|
-
label:
|
|
77
|
-
value:
|
|
78
|
-
icon:
|
|
77
|
+
label: S("planmagnet.projects.metrics.totalProjects", "Total Projects"),
|
|
78
|
+
value: D.total,
|
|
79
|
+
icon: r
|
|
79
80
|
},
|
|
80
81
|
{
|
|
81
|
-
label:
|
|
82
|
-
value:
|
|
83
|
-
icon:
|
|
82
|
+
label: S("planmagnet.projects.metrics.active", "Active"),
|
|
83
|
+
value: D.active,
|
|
84
|
+
icon: a
|
|
84
85
|
},
|
|
85
86
|
{
|
|
86
|
-
label:
|
|
87
|
-
value:
|
|
88
|
-
icon:
|
|
87
|
+
label: S("planmagnet.projects.metrics.completed", "Completed"),
|
|
88
|
+
value: D.completed,
|
|
89
|
+
icon: n
|
|
89
90
|
},
|
|
90
91
|
{
|
|
91
|
-
label:
|
|
92
|
-
value:
|
|
93
|
-
icon:
|
|
92
|
+
label: S("planmagnet.projects.metrics.onHold", "On Hold"),
|
|
93
|
+
value: D.onHold,
|
|
94
|
+
icon: a
|
|
94
95
|
}
|
|
95
|
-
].map((e) => /* @__PURE__ */
|
|
96
|
+
].map((e) => /* @__PURE__ */ l(m, { children: /* @__PURE__ */ u(h, {
|
|
96
97
|
className: "pt-4",
|
|
97
|
-
children: [/* @__PURE__ */
|
|
98
|
+
children: [/* @__PURE__ */ u("div", {
|
|
98
99
|
className: "flex items-center gap-2 text-muted-foreground text-sm",
|
|
99
|
-
children: [/* @__PURE__ */
|
|
100
|
-
}), /* @__PURE__ */
|
|
100
|
+
children: [/* @__PURE__ */ l(e.icon, { className: "h-4 w-4" }), e.label]
|
|
101
|
+
}), /* @__PURE__ */ l("p", {
|
|
101
102
|
className: "text-xl font-semibold mt-1",
|
|
102
103
|
children: e.value
|
|
103
104
|
})]
|
|
104
105
|
}) }, e.label))
|
|
105
106
|
}),
|
|
106
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ l("div", {
|
|
107
108
|
className: "grid grid-cols-1 md:grid-cols-2 gap-4",
|
|
108
|
-
children:
|
|
109
|
+
children: E.map((e) => /* @__PURE__ */ u(m, { children: [/* @__PURE__ */ l(g, {
|
|
109
110
|
className: "pb-2",
|
|
110
|
-
children: /* @__PURE__ */
|
|
111
|
+
children: /* @__PURE__ */ u("div", {
|
|
111
112
|
className: "flex items-center justify-between",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ l(_, {
|
|
113
114
|
className: "text-base",
|
|
114
115
|
children: e.name
|
|
115
|
-
}), /* @__PURE__ */
|
|
116
|
+
}), /* @__PURE__ */ l("span", {
|
|
116
117
|
className: "text-xs text-muted-foreground font-mono",
|
|
117
118
|
children: e.key
|
|
118
119
|
})]
|
|
119
120
|
})
|
|
120
|
-
}), /* @__PURE__ */
|
|
121
|
+
}), /* @__PURE__ */ u(h, {
|
|
121
122
|
className: "space-y-3",
|
|
122
123
|
children: [
|
|
123
|
-
/* @__PURE__ */
|
|
124
|
+
/* @__PURE__ */ l("p", {
|
|
124
125
|
className: "text-sm text-muted-foreground",
|
|
125
|
-
children: e.description ??
|
|
126
|
+
children: e.description ?? S("planmagnet.projects.shared.noDescription", "No description")
|
|
126
127
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ u("div", {
|
|
128
129
|
className: "flex gap-2",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
-
variant:
|
|
130
|
+
children: [/* @__PURE__ */ l(f, {
|
|
131
|
+
variant: b(e.status),
|
|
131
132
|
className: "capitalize",
|
|
132
133
|
children: e.status.toLowerCase().replace("_", " ")
|
|
133
|
-
}), /* @__PURE__ */
|
|
134
|
-
className: `text-xs px-2 py-0.5 rounded-full capitalize ${
|
|
134
|
+
}), /* @__PURE__ */ l("span", {
|
|
135
|
+
className: `text-xs px-2 py-0.5 rounded-full capitalize ${y[e.methodology] ?? "bg-gray-100"}`,
|
|
135
136
|
children: e.methodology.toLowerCase()
|
|
136
137
|
})]
|
|
137
138
|
}),
|
|
138
|
-
e.tags.length > 0 && /* @__PURE__ */
|
|
139
|
+
e.tags.length > 0 && /* @__PURE__ */ l("div", {
|
|
139
140
|
className: "flex gap-1 flex-wrap",
|
|
140
|
-
children: e.tags.map((e) => /* @__PURE__ */
|
|
141
|
+
children: e.tags.map((e) => /* @__PURE__ */ l(f, {
|
|
141
142
|
variant: "outline",
|
|
142
143
|
className: "text-xs",
|
|
143
144
|
children: e
|
|
144
145
|
}, e))
|
|
145
146
|
}),
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ u("div", {
|
|
147
148
|
className: "flex justify-between text-xs text-muted-foreground border-t pt-2",
|
|
148
|
-
children: [/* @__PURE__ */
|
|
149
|
+
children: [/* @__PURE__ */ u("span", {
|
|
149
150
|
className: "flex items-center gap-1",
|
|
150
151
|
children: [
|
|
151
|
-
/* @__PURE__ */
|
|
152
|
-
|
|
152
|
+
/* @__PURE__ */ l(o, { className: "h-3 w-3" }),
|
|
153
|
+
S("planmagnet.projects.shared.owner", "Owner"),
|
|
153
154
|
": ",
|
|
154
155
|
e.ownerId
|
|
155
156
|
]
|
|
156
|
-
}), /* @__PURE__ */
|
|
157
|
+
}), /* @__PURE__ */ u("span", { children: [
|
|
157
158
|
e.startDate ? new Date(e.startDate).toLocaleDateString() : "—",
|
|
158
159
|
" -",
|
|
159
160
|
" ",
|
|
@@ -163,14 +164,14 @@ function b() {
|
|
|
163
164
|
]
|
|
164
165
|
})] }, e.id))
|
|
165
166
|
}),
|
|
166
|
-
|
|
167
|
+
E.length === 0 && /* @__PURE__ */ l("p", {
|
|
167
168
|
className: "text-center text-muted-foreground py-8",
|
|
168
|
-
children:
|
|
169
|
+
children: S("planmagnet.projects.empty", "No projects found.")
|
|
169
170
|
})
|
|
170
171
|
]
|
|
171
172
|
});
|
|
172
173
|
}
|
|
173
174
|
//#endregion
|
|
174
|
-
export {
|
|
175
|
+
export { x as ProjectsProjectsPagePage };
|
|
175
176
|
|
|
176
177
|
//# sourceMappingURL=ProjectsProjectsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsProjectsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { FolderKanban, Plus, Users, CheckSquare, TrendingUp } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst GET_PROJECTS = gql`\n query GetProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n description\n status\n methodology\n ownerId\n startDate\n endDate\n tags\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst methodColor: Record<string, string> = {\n SCRUM: 'bg-blue-100 text-blue-800',\n KANBAN: 'bg-purple-100 text-purple-800',\n WATERFALL: 'bg-gray-100 text-gray-800',\n HYBRID: 'bg-teal-100 text-teal-800',\n AGILE: 'bg-indigo-100 text-indigo-800',\n CUSTOM: 'bg-amber-100 text-amber-800',\n};\n\nconst statusVariant = (status: string) => (status === 'ACTIVE' ? 'default' : 'secondary');\n\nexport function ProjectsProjectsPagePage() {\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\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_PROJECTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n });\n\n if (loading)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return (\n <div className=\"p-6 text-red-500\">\n {t('planmagnet.projects.errorLoading', 'Error loading projects')}: {error.message}\n </div>\n );\n\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const stats = {\n total: projects.length,\n active: projects.filter((p: { status: string }) => p.status === 'ACTIVE').length,\n completed: projects.filter((p: { status: string }) => p.status === 'COMPLETED').length,\n onHold: projects.filter((p: { status: string }) => p.status === 'ON_HOLD').length,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n {t('planmagnet.projects.title', 'All Projects')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('planmagnet.projects.subtitle', 'Manage and track all projects across teams')}\n </p>\n </div>\n <Button size=\"sm\">\n <Plus className=\"mr-1 h-4 w-4\" />\n {t('planmagnet.projects.actions.newProject', 'New Project')}\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n {\n label: t('planmagnet.projects.metrics.totalProjects', 'Total Projects'),\n value: stats.total,\n icon: FolderKanban,\n },\n {\n label: t('planmagnet.projects.metrics.active', 'Active'),\n value: stats.active,\n icon: TrendingUp,\n },\n {\n label: t('planmagnet.projects.metrics.completed', 'Completed'),\n value: stats.completed,\n icon: CheckSquare,\n },\n {\n label: t('planmagnet.projects.metrics.onHold', 'On Hold'),\n value: stats.onHold,\n icon: TrendingUp,\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {projects.map(\n (project: {\n id: string;\n name: string;\n key: string;\n description: string | null;\n status: string;\n methodology: string;\n ownerId: string;\n startDate: string | null;\n endDate: string | null;\n tags: string[];\n }) => {\n return (\n <Card key={project.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{project.name}</CardTitle>\n <span className=\"text-xs text-muted-foreground font-mono\">{project.key}</span>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {project.description ??\n t('planmagnet.projects.shared.noDescription', 'No description')}\n </p>\n <div className=\"flex gap-2\">\n <Badge variant={statusVariant(project.status)} className=\"capitalize\">\n {project.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${methodColor[project.methodology] ?? 'bg-gray-100'}`}\n >\n {project.methodology.toLowerCase()}\n </span>\n </div>\n {project.tags.length > 0 && (\n <div className=\"flex gap-1 flex-wrap\">\n {project.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"flex justify-between text-xs text-muted-foreground border-t pt-2\">\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {t('planmagnet.projects.shared.owner', 'Owner')}: {project.ownerId}\n </span>\n <span>\n {project.startDate ? new Date(project.startDate).toLocaleDateString() : '—'} -{' '}\n {project.endDate ? new Date(project.endDate).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n );\n }\n )}\n </div>\n {projects.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n {t('planmagnet.projects.empty', 'No projects found.')}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AASA,IAAM,IAAe,CAAG;;;;;;;;;;;;;;;;;;;GAqBlB,IAAsC;CAC1C,OAAO;CACP,QAAQ;CACR,WAAW;CACX,QAAQ;CACR,OAAO;CACP,QAAQ;CACT,EAEK,KAAiB,MAAoB,MAAW,WAAW,YAAY;AAE7E,SAAgB,IAA2B;CACzC,IAAM,EAAE,mBAAgB,GAAe,EACjC,EAAE,SAAM,GAAS,EAEjB,EACJ,SACA,YACA,aAGA,EAA8B,GAAc,EAC1C,WAAW,EAAE,aAAa,KAAe,WAAW,EACrD,CAAC;AAEJ,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;EAC9F,CAAA;AAEV,KAAI,EACF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACG,EAAE,oCAAoC,yBAAyB;GAAC;GAAG,EAAM;GACtE;;CAGV,IAAM,IAAW,GAAM,oBAAoB,SAAS,EAAE,EAEhD,IAAQ;EACZ,OAAO,EAAS;EAChB,QAAQ,EAAS,QAAQ,MAA0B,EAAE,WAAW,SAAS,CAAC;EAC1E,WAAW,EAAS,QAAQ,MAA0B,EAAE,WAAW,YAAY,CAAC;EAChF,QAAQ,EAAS,QAAQ,MAA0B,EAAE,WAAW,UAAU,CAAC;EAC5E;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,6BAA6B,eAAe;KAC5C,CAAA,EACL,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,gCAAgC,6CAA6C;KAC9E,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;eAAb,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAChC,EAAE,0CAA0C,cAAc,CACpD;OACL;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MACE,OAAO,EAAE,6CAA6C,iBAAiB;MACvE,OAAO,EAAM;MACb,MAAM;MACP;KACD;MACE,OAAO,EAAE,sCAAsC,SAAS;MACxD,OAAO,EAAM;MACb,MAAM;MACP;KACD;MACE,OAAO,EAAE,yCAAyC,YAAY;MAC9D,OAAO,EAAM;MACb,MAAM;MACP;KACD;MACE,OAAO,EAAE,sCAAsC,UAAU;MACzD,OAAO,EAAM;MACb,MAAM;MACP;KACF,CAAC,KAAK,MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,EAAE,MAAH,EAAQ,WAAU,WAAY,CAAA,EAC7B,EAAE,MACC;SACN,kBAAC,KAAD;MAAG,WAAU;gBAA8B,EAAE;MAAU,CAAA,CAC3C;QACT,EARI,EAAE,MAQN,CACP;IACE,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KACP,MAaG,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;KAAY,WAAU;eACpB,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OAAW,WAAU;iBAAa,EAAQ;OAAiB,CAAA,EAC3D,kBAAC,QAAD;OAAM,WAAU;iBAA2C,EAAQ;OAAW,CAAA,CAC1E;;KACK,CAAA,EACb,kBAAC,GAAD;KAAa,WAAU;eAAvB;MACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAQ,eACP,EAAE,4CAA4C,iBAAiB;OAC/D,CAAA;MACJ,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,GAAD;QAAO,SAAS,EAAc,EAAQ,OAAO;QAAE,WAAU;kBACtD,EAAQ,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;QACzC,CAAA,EACR,kBAAC,QAAD;QACE,WAAW,+CAA+C,EAAY,EAAQ,gBAAgB;kBAE7F,EAAQ,YAAY,aAAa;QAC7B,CAAA,CACH;;MACL,EAAQ,KAAK,SAAS,KACrB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAQ,KAAK,KAAK,MACjB,kBAAC,GAAD;QAAiB,SAAQ;QAAU,WAAU;kBAC1C;QACK,EAFI,EAEJ,CACR;OACE,CAAA;MAER,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACE,kBAAC,GAAD,EAAO,WAAU,WAAY,CAAA;SAC5B,EAAE,oCAAoC,QAAQ;SAAC;SAAG,EAAQ;SACtD;WACP,kBAAC,QAAD,EAAA,UAAA;QACG,EAAQ,YAAY,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,GAAG;QAAI;QAAG;QAC9E,EAAQ,UAAU,IAAI,KAAK,EAAQ,QAAQ,CAAC,oBAAoB,GAAG;QAC/D,EAAA,CAAA,CACH;;MACM;OACT,EAAA,EA1CI,EAAQ,GA0CZ,CAGZ;IACG,CAAA;GACL,EAAS,WAAW,KACnB,kBAAC,KAAD;IAAG,WAAU;cACV,EAAE,6BAA6B,qBAAqB;IACnD,CAAA;GAEF"}
|
|
1
|
+
{"version":3,"file":"ProjectsProjectsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsProjectsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { FolderKanban, Plus, Users, CheckSquare, TrendingUp } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst GET_PROJECTS = gql`\n query GetProjects($workspaceId: ID!) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n key\n description\n status\n methodology\n ownerId\n startDate\n endDate\n tags\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst methodColor: Record<string, string> = {\n SCRUM: 'bg-blue-100 text-blue-800',\n KANBAN: 'bg-purple-100 text-purple-800',\n WATERFALL: 'bg-gray-100 text-gray-800',\n HYBRID: 'bg-teal-100 text-teal-800',\n AGILE: 'bg-indigo-100 text-indigo-800',\n CUSTOM: 'bg-amber-100 text-amber-800',\n};\n\nconst statusVariant = (status: string) => (status === 'ACTIVE' ? 'default' : 'secondary');\n\nexport function ProjectsProjectsPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const { t } = useI18n();\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_PROJECTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n });\n\n if (loading)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return (\n <div className=\"p-6 text-red-500\">\n {t('planmagnet.projects.errorLoading', 'Error loading projects')}: {error.message}\n </div>\n );\n\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const stats = {\n total: projects.length,\n active: projects.filter((p: { status: string }) => p.status === 'ACTIVE').length,\n completed: projects.filter((p: { status: string }) => p.status === 'COMPLETED').length,\n onHold: projects.filter((p: { status: string }) => p.status === 'ON_HOLD').length,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n {t('planmagnet.projects.title', 'All Projects')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {t('planmagnet.projects.subtitle', 'Manage and track all projects across teams')}\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/projects/create-project-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n {t('planmagnet.projects.actions.newProject', 'New Project')}\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n {\n label: t('planmagnet.projects.metrics.totalProjects', 'Total Projects'),\n value: stats.total,\n icon: FolderKanban,\n },\n {\n label: t('planmagnet.projects.metrics.active', 'Active'),\n value: stats.active,\n icon: TrendingUp,\n },\n {\n label: t('planmagnet.projects.metrics.completed', 'Completed'),\n value: stats.completed,\n icon: CheckSquare,\n },\n {\n label: t('planmagnet.projects.metrics.onHold', 'On Hold'),\n value: stats.onHold,\n icon: TrendingUp,\n },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {projects.map(\n (project: {\n id: string;\n name: string;\n key: string;\n description: string | null;\n status: string;\n methodology: string;\n ownerId: string;\n startDate: string | null;\n endDate: string | null;\n tags: string[];\n }) => {\n return (\n <Card key={project.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{project.name}</CardTitle>\n <span className=\"text-xs text-muted-foreground font-mono\">{project.key}</span>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n {project.description ??\n t('planmagnet.projects.shared.noDescription', 'No description')}\n </p>\n <div className=\"flex gap-2\">\n <Badge variant={statusVariant(project.status)} className=\"capitalize\">\n {project.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${methodColor[project.methodology] ?? 'bg-gray-100'}`}\n >\n {project.methodology.toLowerCase()}\n </span>\n </div>\n {project.tags.length > 0 && (\n <div className=\"flex gap-1 flex-wrap\">\n {project.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n <div className=\"flex justify-between text-xs text-muted-foreground border-t pt-2\">\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {t('planmagnet.projects.shared.owner', 'Owner')}: {project.ownerId}\n </span>\n <span>\n {project.startDate ? new Date(project.startDate).toLocaleDateString() : '—'} -{' '}\n {project.endDate ? new Date(project.endDate).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n );\n }\n )}\n </div>\n {projects.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n {t('planmagnet.projects.empty', 'No projects found.')}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportsReportsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,sBAAsB,
|
|
1
|
+
{"version":3,"file":"ReportsReportsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,sBAAsB,4CAsKrC"}
|
|
@@ -117,7 +117,7 @@ function v() {
|
|
|
117
117
|
children: [A.length, " reports configured"]
|
|
118
118
|
})] }), /* @__PURE__ */ u(d, {
|
|
119
119
|
size: "sm",
|
|
120
|
-
onClick: () => y("/reports/create"),
|
|
120
|
+
onClick: () => y("/reports/create-report-page"),
|
|
121
121
|
children: [/* @__PURE__ */ l(o, { className: "mr-1 h-4 w-4" }), "New Report"]
|
|
122
122
|
})]
|
|
123
123
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportsReportsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button } from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Plus, Clock, Calendar } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst GET_REPORTS_DATA = gql`\n query GetReportsData(\n $workspaceId: ID!\n $projectPagination: PlanmagnetPaginationInput\n $sprintPagination: PlanmagnetPaginationInput\n $workItemPagination: PlanmagnetPaginationInput\n ) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: $projectPagination) {\n items {\n id\n name\n key\n status\n methodology\n startDate\n endDate\n }\n totalCount\n }\n planmagnetSprints(workspaceId: $workspaceId, pagination: $sprintPagination) {\n items {\n id\n name\n status\n velocity\n startDate\n endDate\n }\n totalCount\n }\n planmagnetWorkItems(workspaceId: $workspaceId, pagination: $workItemPagination) {\n items {\n id\n status\n storyPoints\n createdAt\n }\n totalCount\n }\n }\n`;\n\ninterface ProjectNode {\n id: string;\n name: string;\n key: string;\n status: string;\n methodology: string | null;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n status: string;\n velocity: number | null;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface WorkItemNode {\n id: string;\n status: string;\n storyPoints: number | null;\n createdAt: string;\n}\n\ninterface ReportsData {\n projects: { items: ProjectNode[]; totalCount: number };\n sprints: { items: SprintNode[]; totalCount: number };\n workItems: { items: WorkItemNode[]; totalCount: number };\n}\n\nconst typeColor: Record<string, string> = {\n velocity: 'bg-blue-100 text-blue-800',\n burndown: 'bg-orange-100 text-orange-800',\n burnup: 'bg-green-100 text-green-800',\n cumulative_flow: 'bg-purple-100 text-purple-800',\n cycle_time: 'bg-yellow-100 text-yellow-800',\n lead_time: 'bg-pink-100 text-pink-800',\n project_status: 'bg-gray-100 text-gray-800',\n};\n\nexport function ReportsReportsPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [typeFilter, setTypeFilter] = useState<string>('all');\n\n const { data, loading, error } = useQuery<ReportsData>(GET_REPORTS_DATA, {\n variables: {\n workspaceId,\n projectPagination: { first: 100 },\n sprintPagination: { first: 100 },\n workItemPagination: { first: 500 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const projects = data?.planmagnetProjects?.items ?? [];\n const sprints = data?.planmagnetSprints?.items ?? [];\n const workItems = data?.planmagnetWorkItems?.items ?? [];\n\n const completedSprints = sprints.filter((s) => s.status === 'COMPLETED');\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE');\n const creatorName = currentUser?.name ?? currentUser?.firstName ?? 'System';\n\n const reports = [\n ...completedSprints.map((sprint) => ({\n id: `velocity-${sprint.id}`,\n name: `${sprint.name} Velocity Report`,\n description: `Velocity analysis for ${sprint.name} (${sprint.velocity ?? 0} pts)`,\n type: 'velocity',\n createdBy: creatorName,\n lastRun: sprint.endDate ?? '',\n schedule: null as string | null,\n })),\n ...activeSprints.map((sprint) => ({\n id: `burndown-${sprint.id}`,\n name: `${sprint.name} Burndown`,\n description: `Active burndown tracking for ${sprint.name}`,\n type: 'burndown',\n createdBy: creatorName,\n lastRun: new Date().toISOString().split('T')[0],\n schedule: 'daily' as string | null,\n })),\n ...projects\n .filter((p) => p.status === 'ACTIVE')\n .map((project) => ({\n id: `status-${project.id}`,\n name: `${project.name} Status Report`,\n description: `Project status overview for ${project.key}`,\n type: 'project_status',\n createdBy: creatorName,\n lastRun: new Date().toISOString().split('T')[0],\n schedule: 'weekly' as string | null,\n })),\n ];\n\n const types = ['all', ...new Set(reports.map((r) => r.type))];\n const filtered = typeFilter === 'all' ? reports : reports.filter((r) => r.type === typeFilter);\n\n const scheduledCount = reports.filter((r) => r.schedule).length;\n const todayStr = new Date().toISOString().split('T')[0];\n const runTodayCount = reports.filter((r) => r.lastRun === todayStr).length;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Reports</h1>\n <p className=\"text-sm text-muted-foreground\">{reports.length} reports configured</p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/reports/create')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Report\n </Button>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Reports</p>\n <p className=\"text-xl font-bold\">{reports.length}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Scheduled</p>\n <p className=\"text-xl font-bold\">{scheduledCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Run Today</p>\n <p className=\"text-xl font-bold\">{runTodayCount}</p>\n </CardContent>\n </Card>\n </div>\n <div className=\"flex gap-1\">\n {types.map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-3 py-1.5 text-sm rounded-md capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {t === 'all' ? 'All' : t.replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {filtered.map((report) => (\n <Card key={report.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-primary\" />\n {report.name}\n </CardTitle>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${typeColor[report.type] ?? 'bg-gray-100'}`}\n >\n {report.type.replace('_', ' ')}\n </span>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{report.description}</p>\n <div className=\"flex gap-3 text-xs text-muted-foreground\">\n {report.schedule && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n {report.schedule}\n </span>\n )}\n {report.lastRun && (\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Last: {report.lastRun}\n </span>\n )}\n <span className=\"flex items-center gap-1\">{report.createdBy}</span>\n </div>\n </CardContent>\n </Card>\n ))}\n {filtered.length === 0 && (\n <div className=\"col-span-2 text-center py-12\">\n <BarChart3 className=\"h-12 w-12 text-muted-foreground mx-auto mb-3\" />\n <p className=\"text-sm text-muted-foreground\">\n No reports found for the selected filter.\n </p>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ReportsReportsPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button } from '@burdenoff/fe-libs/ui';\nimport { BarChart3, Plus, Clock, Calendar } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\nconst GET_REPORTS_DATA = gql`\n query GetReportsData(\n $workspaceId: ID!\n $projectPagination: PlanmagnetPaginationInput\n $sprintPagination: PlanmagnetPaginationInput\n $workItemPagination: PlanmagnetPaginationInput\n ) {\n planmagnetProjects(workspaceId: $workspaceId, pagination: $projectPagination) {\n items {\n id\n name\n key\n status\n methodology\n startDate\n endDate\n }\n totalCount\n }\n planmagnetSprints(workspaceId: $workspaceId, pagination: $sprintPagination) {\n items {\n id\n name\n status\n velocity\n startDate\n endDate\n }\n totalCount\n }\n planmagnetWorkItems(workspaceId: $workspaceId, pagination: $workItemPagination) {\n items {\n id\n status\n storyPoints\n createdAt\n }\n totalCount\n }\n }\n`;\n\ninterface ProjectNode {\n id: string;\n name: string;\n key: string;\n status: string;\n methodology: string | null;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n status: string;\n velocity: number | null;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface WorkItemNode {\n id: string;\n status: string;\n storyPoints: number | null;\n createdAt: string;\n}\n\ninterface ReportsData {\n projects: { items: ProjectNode[]; totalCount: number };\n sprints: { items: SprintNode[]; totalCount: number };\n workItems: { items: WorkItemNode[]; totalCount: number };\n}\n\nconst typeColor: Record<string, string> = {\n velocity: 'bg-blue-100 text-blue-800',\n burndown: 'bg-orange-100 text-orange-800',\n burnup: 'bg-green-100 text-green-800',\n cumulative_flow: 'bg-purple-100 text-purple-800',\n cycle_time: 'bg-yellow-100 text-yellow-800',\n lead_time: 'bg-pink-100 text-pink-800',\n project_status: 'bg-gray-100 text-gray-800',\n};\n\nexport function ReportsReportsPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n const [typeFilter, setTypeFilter] = useState<string>('all');\n\n const { data, loading, error } = useQuery<ReportsData>(GET_REPORTS_DATA, {\n variables: {\n workspaceId,\n projectPagination: { first: 100 },\n sprintPagination: { first: 100 },\n workItemPagination: { first: 500 },\n },\n skip: !workspaceId,\n });\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-red-500\">Error: {error.message}</div>;\n }\n\n const projects = data?.planmagnetProjects?.items ?? [];\n const sprints = data?.planmagnetSprints?.items ?? [];\n const workItems = data?.planmagnetWorkItems?.items ?? [];\n\n const completedSprints = sprints.filter((s) => s.status === 'COMPLETED');\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE');\n const creatorName = currentUser?.name ?? currentUser?.firstName ?? 'System';\n\n const reports = [\n ...completedSprints.map((sprint) => ({\n id: `velocity-${sprint.id}`,\n name: `${sprint.name} Velocity Report`,\n description: `Velocity analysis for ${sprint.name} (${sprint.velocity ?? 0} pts)`,\n type: 'velocity',\n createdBy: creatorName,\n lastRun: sprint.endDate ?? '',\n schedule: null as string | null,\n })),\n ...activeSprints.map((sprint) => ({\n id: `burndown-${sprint.id}`,\n name: `${sprint.name} Burndown`,\n description: `Active burndown tracking for ${sprint.name}`,\n type: 'burndown',\n createdBy: creatorName,\n lastRun: new Date().toISOString().split('T')[0],\n schedule: 'daily' as string | null,\n })),\n ...projects\n .filter((p) => p.status === 'ACTIVE')\n .map((project) => ({\n id: `status-${project.id}`,\n name: `${project.name} Status Report`,\n description: `Project status overview for ${project.key}`,\n type: 'project_status',\n createdBy: creatorName,\n lastRun: new Date().toISOString().split('T')[0],\n schedule: 'weekly' as string | null,\n })),\n ];\n\n const types = ['all', ...new Set(reports.map((r) => r.type))];\n const filtered = typeFilter === 'all' ? reports : reports.filter((r) => r.type === typeFilter);\n\n const scheduledCount = reports.filter((r) => r.schedule).length;\n const todayStr = new Date().toISOString().split('T')[0];\n const runTodayCount = reports.filter((r) => r.lastRun === todayStr).length;\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Reports</h1>\n <p className=\"text-sm text-muted-foreground\">{reports.length} reports configured</p>\n </div>\n {/* The route is `reports/create-report-page`; `/reports/create` has never\n existed, so this button navigated into the catch-all. */}\n <Button size=\"sm\" onClick={() => navigate('/reports/create-report-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Report\n </Button>\n </div>\n <div className=\"grid grid-cols-3 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Reports</p>\n <p className=\"text-xl font-bold\">{reports.length}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Scheduled</p>\n <p className=\"text-xl font-bold\">{scheduledCount}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Run Today</p>\n <p className=\"text-xl font-bold\">{runTodayCount}</p>\n </CardContent>\n </Card>\n </div>\n <div className=\"flex gap-1\">\n {types.map((t) => (\n <button\n key={t}\n onClick={() => setTypeFilter(t)}\n className={`px-3 py-1.5 text-sm rounded-md capitalize ${typeFilter === t ? 'bg-primary text-primary-foreground' : 'text-muted-foreground hover:bg-muted'}`}\n >\n {t === 'all' ? 'All' : t.replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {filtered.map((report) => (\n <Card key={report.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-primary\" />\n {report.name}\n </CardTitle>\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${typeColor[report.type] ?? 'bg-gray-100'}`}\n >\n {report.type.replace('_', ' ')}\n </span>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{report.description}</p>\n <div className=\"flex gap-3 text-xs text-muted-foreground\">\n {report.schedule && (\n <span className=\"flex items-center gap-1\">\n <Clock className=\"h-3 w-3\" />\n {report.schedule}\n </span>\n )}\n {report.lastRun && (\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n Last: {report.lastRun}\n </span>\n )}\n <span className=\"flex items-center gap-1\">{report.createdBy}</span>\n </div>\n </CardContent>\n </Card>\n ))}\n {filtered.length === 0 && (\n <div className=\"col-span-2 text-center py-12\">\n <BarChart3 className=\"h-12 w-12 text-muted-foreground mx-auto mb-3\" />\n <p className=\"text-sm text-muted-foreground\">\n No reports found for the selected filter.\n </p>\n </div>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-planmagnet",
|
|
3
|
-
"version": "2026.
|
|
3
|
+
"version": "2026.827.1",
|
|
4
4
|
"description": "Burdenoff PlanMagnet Microfrontend",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -98,7 +98,7 @@
|
|
|
98
98
|
"zustand": "^5.0.0"
|
|
99
99
|
},
|
|
100
100
|
"publishConfig": {
|
|
101
|
-
"access": "
|
|
101
|
+
"access": "public"
|
|
102
102
|
},
|
|
103
103
|
"overrides": {
|
|
104
104
|
"lodash": ">=4.17.21",
|