@burdenoff/microfe-planmagnet 2026.830.1 → 2026.923.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +52 -0
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/hooks/useEntityId.d.ts +14 -0
- package/dist/planmagnet/hooks/useEntityId.d.ts.map +1 -0
- package/dist/planmagnet/hooks/useEntityId.js +15 -0
- package/dist/planmagnet/hooks/useEntityId.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +148 -144
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -84
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +95 -91
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +117 -113
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +74 -70
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +21 -17
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +21 -16
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +67 -63
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +61 -56
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +107 -99
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +94 -90
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +46 -41
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +110 -106
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +2 -2
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +2 -2
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +67 -66
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/utils/rowNavigation.d.ts +29 -0
- package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -0
- package/dist/planmagnet/utils/rowNavigation.js +20 -0
- package/dist/planmagnet/utils/rowNavigation.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
2
|
+
import { detailPath as n, rowLinkProps as r } from "../utils/rowNavigation.js";
|
|
3
|
+
import { useState as i } from "react";
|
|
4
|
+
import { ArrowUpDown as a, Filter as o, Loader2 as s, Plus as c, Star as l, Target as u, ThumbsUp as d } from "lucide-react";
|
|
5
|
+
import { gql as f } from "@apollo/client";
|
|
6
|
+
import { useQuery as p } from "@apollo/client/react";
|
|
7
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
8
|
+
import { Badge as g, Button as _, Card as v, CardContent as y } from "@burdenoff/fe-libs/ui";
|
|
8
9
|
//#region src/planmagnet/pages/ProductsFeaturesPagePage.tsx
|
|
9
|
-
var
|
|
10
|
+
var b = f`
|
|
10
11
|
query ListFeatureRequests(
|
|
11
12
|
$workspaceId: ID!
|
|
12
13
|
$filter: PlanmagnetFeatureRequestFilter
|
|
@@ -30,7 +31,7 @@ var v = u`
|
|
|
30
31
|
totalCount
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
|
-
`,
|
|
34
|
+
`, x = {
|
|
34
35
|
SUBMITTED: "bg-gray-100 text-gray-800",
|
|
35
36
|
UNDER_REVIEW: "bg-yellow-100 text-yellow-800",
|
|
36
37
|
PLANNED: "bg-blue-100 text-blue-800",
|
|
@@ -39,27 +40,27 @@ var v = u`
|
|
|
39
40
|
DECLINED: "bg-red-100 text-red-800",
|
|
40
41
|
DUPLICATE: "bg-orange-100 text-orange-800"
|
|
41
42
|
};
|
|
42
|
-
function
|
|
43
|
-
let
|
|
43
|
+
function S() {
|
|
44
|
+
let f = t(), { workspaceId: S } = e(), [C, w] = i("all"), [T, E] = i("priorityScore"), { data: D, loading: O, error: k } = p(b, {
|
|
44
45
|
variables: {
|
|
45
|
-
workspaceId:
|
|
46
|
-
filter:
|
|
46
|
+
workspaceId: S,
|
|
47
|
+
filter: C === "all" ? void 0 : { status: C },
|
|
47
48
|
pagination: { first: 50 }
|
|
48
49
|
},
|
|
49
|
-
skip: !
|
|
50
|
+
skip: !S
|
|
50
51
|
});
|
|
51
|
-
if (
|
|
52
|
+
if (O) return /* @__PURE__ */ m("div", {
|
|
52
53
|
className: "flex items-center justify-center p-12",
|
|
53
|
-
children: /* @__PURE__ */
|
|
54
|
+
children: /* @__PURE__ */ m(s, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
54
55
|
});
|
|
55
|
-
if (
|
|
56
|
+
if (k) return /* @__PURE__ */ h("div", {
|
|
56
57
|
className: "p-6 text-destructive",
|
|
57
|
-
children: ["Failed to load features: ",
|
|
58
|
+
children: ["Failed to load features: ", k.message]
|
|
58
59
|
});
|
|
59
|
-
let
|
|
60
|
-
let n = e[
|
|
61
|
-
return (t[
|
|
62
|
-
}),
|
|
60
|
+
let A = D?.planmagnetFeatureRequests?.items ?? [], j = D?.planmagnetFeatureRequests?.totalCount ?? 0, M = [...A].sort((e, t) => {
|
|
61
|
+
let n = e[T] ?? 0;
|
|
62
|
+
return (t[T] ?? 0) - n;
|
|
63
|
+
}), N = [
|
|
63
64
|
"all",
|
|
64
65
|
"SUBMITTED",
|
|
65
66
|
"UNDER_REVIEW",
|
|
@@ -68,184 +69,187 @@ function b() {
|
|
|
68
69
|
"COMPLETED",
|
|
69
70
|
"DECLINED",
|
|
70
71
|
"DUPLICATE"
|
|
71
|
-
],
|
|
72
|
-
total:
|
|
73
|
-
inProgress:
|
|
74
|
-
completed:
|
|
75
|
-
avgPriority:
|
|
72
|
+
], P = {
|
|
73
|
+
total: j,
|
|
74
|
+
inProgress: A.filter((e) => e.status === "IN_PROGRESS").length,
|
|
75
|
+
completed: A.filter((e) => e.status === "COMPLETED").length,
|
|
76
|
+
avgPriority: A.length > 0 ? Math.round(A.reduce((e, t) => e + (t.priorityScore ?? 0), 0) / A.length) : 0
|
|
76
77
|
};
|
|
77
|
-
return /* @__PURE__ */
|
|
78
|
+
return /* @__PURE__ */ h("div", {
|
|
78
79
|
className: "space-y-6 p-6",
|
|
79
80
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ h("div", {
|
|
81
82
|
className: "flex items-center justify-between",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("h1", {
|
|
83
84
|
className: "text-2xl font-semibold",
|
|
84
85
|
children: "Features"
|
|
85
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ h("p", {
|
|
86
87
|
className: "text-sm text-muted-foreground",
|
|
87
|
-
children: [
|
|
88
|
-
})] }), /* @__PURE__ */
|
|
88
|
+
children: [j, " feature requests across all products"]
|
|
89
|
+
})] }), /* @__PURE__ */ h(_, {
|
|
89
90
|
size: "sm",
|
|
90
|
-
onClick: () =>
|
|
91
|
-
children: [/* @__PURE__ */
|
|
91
|
+
onClick: () => f("/products/new-feature-page"),
|
|
92
|
+
children: [/* @__PURE__ */ m(c, { className: "mr-1 h-4 w-4" }), "New Feature"]
|
|
92
93
|
})]
|
|
93
94
|
}),
|
|
94
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ h("div", {
|
|
95
96
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
96
97
|
children: [
|
|
97
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
|
|
98
99
|
className: "pt-4",
|
|
99
|
-
children: [/* @__PURE__ */
|
|
100
|
+
children: [/* @__PURE__ */ m("p", {
|
|
100
101
|
className: "text-xs text-muted-foreground",
|
|
101
102
|
children: "Total Features"
|
|
102
|
-
}), /* @__PURE__ */
|
|
103
|
+
}), /* @__PURE__ */ m("p", {
|
|
103
104
|
className: "text-xl font-bold",
|
|
104
|
-
children:
|
|
105
|
+
children: P.total
|
|
105
106
|
})]
|
|
106
107
|
}) }),
|
|
107
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
|
|
108
109
|
className: "pt-4",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
110
|
+
children: [/* @__PURE__ */ m("p", {
|
|
110
111
|
className: "text-xs text-muted-foreground",
|
|
111
112
|
children: "In Progress"
|
|
112
|
-
}), /* @__PURE__ */
|
|
113
|
+
}), /* @__PURE__ */ m("p", {
|
|
113
114
|
className: "text-xl font-bold",
|
|
114
|
-
children:
|
|
115
|
+
children: P.inProgress
|
|
115
116
|
})]
|
|
116
117
|
}) }),
|
|
117
|
-
/* @__PURE__ */
|
|
118
|
+
/* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
|
|
118
119
|
className: "pt-4",
|
|
119
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ m("p", {
|
|
120
121
|
className: "text-xs text-muted-foreground",
|
|
121
122
|
children: "Completed"
|
|
122
|
-
}), /* @__PURE__ */
|
|
123
|
+
}), /* @__PURE__ */ m("p", {
|
|
123
124
|
className: "text-xl font-bold",
|
|
124
|
-
children:
|
|
125
|
+
children: P.completed
|
|
125
126
|
})]
|
|
126
127
|
}) }),
|
|
127
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ m(v, { children: /* @__PURE__ */ h(y, {
|
|
128
129
|
className: "pt-4",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ m("p", {
|
|
130
131
|
className: "text-xs text-muted-foreground",
|
|
131
132
|
children: "Avg Priority Score"
|
|
132
|
-
}), /* @__PURE__ */
|
|
133
|
+
}), /* @__PURE__ */ m("p", {
|
|
133
134
|
className: "text-xl font-bold",
|
|
134
|
-
children:
|
|
135
|
+
children: P.avgPriority
|
|
135
136
|
})]
|
|
136
137
|
}) })
|
|
137
138
|
]
|
|
138
139
|
}),
|
|
139
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ h("div", {
|
|
140
141
|
className: "flex items-center justify-between flex-wrap gap-2",
|
|
141
|
-
children: [/* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ h("div", {
|
|
142
143
|
className: "flex items-center gap-2",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
144
|
-
onClick: () =>
|
|
145
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
144
|
+
children: [/* @__PURE__ */ m(o, { className: "h-4 w-4 text-muted-foreground" }), N.map((e) => /* @__PURE__ */ m("button", {
|
|
145
|
+
onClick: () => w(e),
|
|
146
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${C === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
146
147
|
children: e === "all" ? "All" : e.toLowerCase().replace("_", " ")
|
|
147
148
|
}, e))]
|
|
148
|
-
}), /* @__PURE__ */
|
|
149
|
+
}), /* @__PURE__ */ h("div", {
|
|
149
150
|
className: "flex items-center gap-2",
|
|
150
|
-
children: [/* @__PURE__ */
|
|
151
|
+
children: [/* @__PURE__ */ m(a, { className: "h-4 w-4 text-muted-foreground" }), [
|
|
151
152
|
"priorityScore",
|
|
152
153
|
"voteCount",
|
|
153
154
|
"businessValue"
|
|
154
|
-
].map((e) => /* @__PURE__ */
|
|
155
|
-
onClick: () =>
|
|
156
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
155
|
+
].map((e) => /* @__PURE__ */ m("button", {
|
|
156
|
+
onClick: () => E(e),
|
|
157
|
+
className: `px-2 py-1 text-xs rounded-md capitalize ${T === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
|
|
157
158
|
children: e === "priorityScore" ? "Priority" : e === "voteCount" ? "Votes" : "Value"
|
|
158
159
|
}, e))]
|
|
159
160
|
})]
|
|
160
161
|
}),
|
|
161
|
-
/* @__PURE__ */
|
|
162
|
+
/* @__PURE__ */ m("div", {
|
|
162
163
|
className: "space-y-3",
|
|
163
|
-
children:
|
|
164
|
-
|
|
165
|
-
children: /* @__PURE__ */
|
|
166
|
-
className: "
|
|
167
|
-
children:
|
|
168
|
-
className: "flex-
|
|
169
|
-
children: [
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
className: "
|
|
174
|
-
children:
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
/* @__PURE__ */ f("p", {
|
|
181
|
-
className: "text-xs text-muted-foreground",
|
|
182
|
-
children: e.description
|
|
183
|
-
}),
|
|
184
|
-
/* @__PURE__ */ p("div", {
|
|
185
|
-
className: "flex items-center gap-4 text-xs text-muted-foreground",
|
|
186
|
-
children: [
|
|
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() })
|
|
190
|
-
]
|
|
191
|
-
}),
|
|
192
|
-
e.tags.length > 0 && /* @__PURE__ */ f("div", {
|
|
193
|
-
className: "flex gap-1",
|
|
194
|
-
children: e.tags.map((e) => /* @__PURE__ */ f(m, {
|
|
195
|
-
variant: "outline",
|
|
196
|
-
className: "text-[10px]",
|
|
197
|
-
children: e
|
|
198
|
-
}, e))
|
|
199
|
-
})
|
|
200
|
-
]
|
|
201
|
-
}), /* @__PURE__ */ p("div", {
|
|
202
|
-
className: "flex items-center gap-4 shrink-0",
|
|
203
|
-
children: [
|
|
204
|
-
/* @__PURE__ */ p("div", {
|
|
205
|
-
className: "text-center",
|
|
206
|
-
children: [/* @__PURE__ */ p("div", {
|
|
207
|
-
className: "flex items-center gap-1",
|
|
208
|
-
children: [/* @__PURE__ */ f(c, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ f("span", {
|
|
209
|
-
className: "text-sm font-bold",
|
|
210
|
-
children: e.priorityScore ?? 0
|
|
164
|
+
children: M.map((e) => /* @__PURE__ */ m(v, {
|
|
165
|
+
...r(() => f(n("/products/feature-detail-page", e.id)), e.title),
|
|
166
|
+
children: /* @__PURE__ */ m(y, {
|
|
167
|
+
className: "py-4",
|
|
168
|
+
children: /* @__PURE__ */ h("div", {
|
|
169
|
+
className: "flex items-start justify-between gap-4",
|
|
170
|
+
children: [/* @__PURE__ */ h("div", {
|
|
171
|
+
className: "flex-1 space-y-2",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ h("div", {
|
|
174
|
+
className: "flex items-center gap-2",
|
|
175
|
+
children: [/* @__PURE__ */ m("span", {
|
|
176
|
+
className: "text-sm font-medium",
|
|
177
|
+
children: e.title
|
|
178
|
+
}), /* @__PURE__ */ m("span", {
|
|
179
|
+
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${x[e.status] ?? "bg-gray-100"}`,
|
|
180
|
+
children: e.status.toLowerCase().replace("_", " ")
|
|
211
181
|
})]
|
|
212
|
-
}),
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ m("p", {
|
|
184
|
+
className: "text-xs text-muted-foreground",
|
|
185
|
+
children: e.description
|
|
186
|
+
}),
|
|
187
|
+
/* @__PURE__ */ h("div", {
|
|
188
|
+
className: "flex items-center gap-4 text-xs text-muted-foreground",
|
|
189
|
+
children: [
|
|
190
|
+
/* @__PURE__ */ h("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
|
|
191
|
+
/* @__PURE__ */ h("span", { children: ["Submitted by: ", e.submittedBy] }),
|
|
192
|
+
/* @__PURE__ */ m("span", { children: new Date(e.updatedAt).toLocaleDateString() })
|
|
193
|
+
]
|
|
194
|
+
}),
|
|
195
|
+
e.tags.length > 0 && /* @__PURE__ */ m("div", {
|
|
196
|
+
className: "flex gap-1",
|
|
197
|
+
children: e.tags.map((e) => /* @__PURE__ */ m(g, {
|
|
198
|
+
variant: "outline",
|
|
199
|
+
className: "text-[10px]",
|
|
200
|
+
children: e
|
|
201
|
+
}, e))
|
|
202
|
+
})
|
|
203
|
+
]
|
|
204
|
+
}), /* @__PURE__ */ h("div", {
|
|
205
|
+
className: "flex items-center gap-4 shrink-0",
|
|
206
|
+
children: [
|
|
207
|
+
/* @__PURE__ */ h("div", {
|
|
208
|
+
className: "text-center",
|
|
209
|
+
children: [/* @__PURE__ */ h("div", {
|
|
210
|
+
className: "flex items-center gap-1",
|
|
211
|
+
children: [/* @__PURE__ */ m(u, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ m("span", {
|
|
212
|
+
className: "text-sm font-bold",
|
|
213
|
+
children: e.priorityScore ?? 0
|
|
214
|
+
})]
|
|
215
|
+
}), /* @__PURE__ */ m("p", {
|
|
216
|
+
className: "text-[10px] text-muted-foreground",
|
|
217
|
+
children: "Priority"
|
|
224
218
|
})]
|
|
225
|
-
}),
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
className: "text-
|
|
236
|
-
children:
|
|
219
|
+
}),
|
|
220
|
+
/* @__PURE__ */ h("div", {
|
|
221
|
+
className: "text-center",
|
|
222
|
+
children: [/* @__PURE__ */ h("div", {
|
|
223
|
+
className: "flex items-center gap-1",
|
|
224
|
+
children: [/* @__PURE__ */ m(d, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ m("span", {
|
|
225
|
+
className: "text-sm font-bold",
|
|
226
|
+
children: e.voteCount
|
|
227
|
+
})]
|
|
228
|
+
}), /* @__PURE__ */ m("p", {
|
|
229
|
+
className: "text-[10px] text-muted-foreground",
|
|
230
|
+
children: "Votes"
|
|
237
231
|
})]
|
|
238
|
-
}),
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
232
|
+
}),
|
|
233
|
+
/* @__PURE__ */ h("div", {
|
|
234
|
+
className: "text-center",
|
|
235
|
+
children: [/* @__PURE__ */ h("div", {
|
|
236
|
+
className: "flex items-center gap-1",
|
|
237
|
+
children: [/* @__PURE__ */ m(l, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ m("span", {
|
|
238
|
+
className: "text-sm font-bold",
|
|
239
|
+
children: e.businessValue ?? 0
|
|
240
|
+
})]
|
|
241
|
+
}), /* @__PURE__ */ m("p", {
|
|
242
|
+
className: "text-[10px] text-muted-foreground",
|
|
243
|
+
children: "Value"
|
|
244
|
+
})]
|
|
245
|
+
})
|
|
246
|
+
]
|
|
247
|
+
})]
|
|
248
|
+
})
|
|
245
249
|
})
|
|
246
|
-
}
|
|
250
|
+
}, e.id))
|
|
247
251
|
}),
|
|
248
|
-
|
|
252
|
+
M.length === 0 && /* @__PURE__ */ m("p", {
|
|
249
253
|
className: "text-center text-muted-foreground py-8",
|
|
250
254
|
children: "No features match the selected filters."
|
|
251
255
|
})
|
|
@@ -253,6 +257,6 @@ function b() {
|
|
|
253
257
|
});
|
|
254
258
|
}
|
|
255
259
|
//#endregion
|
|
256
|
-
export {
|
|
260
|
+
export { S as ProductsFeaturesPagePage };
|
|
257
261
|
|
|
258
262
|
//# 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, 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":"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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';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\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\n key={feature.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/feature-detail-page', feature.id)),\n feature.title\n )}\n >\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AAiEA,wBAAgB,wBAAwB,4CA6LvC"}
|