@burdenoff/microfe-planmagnet 2026.930.23 → 2026.930.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. package/dist/index.js +6 -5
  2. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.js +154 -147
  4. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +66 -62
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/controls/FeatureControls.d.ts +11 -0
  9. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  10. package/dist/planmagnet/controls/FeatureControls.js +305 -135
  11. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  12. package/dist/planmagnet/controls/Pickers.d.ts +18 -0
  13. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  14. package/dist/planmagnet/controls/Pickers.js +78 -1
  15. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  16. package/dist/planmagnet/controls/PrioritizationControls.d.ts +72 -0
  17. package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -0
  18. package/dist/planmagnet/controls/PrioritizationControls.js +246 -0
  19. package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -0
  20. package/dist/planmagnet/index.d.ts +1 -0
  21. package/dist/planmagnet/index.d.ts.map +1 -1
  22. package/dist/planmagnet/index.js +1 -0
  23. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  24. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +131 -125
  25. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  26. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  27. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +183 -99
  28. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  29. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  30. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +113 -97
  31. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  32. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts +2 -0
  33. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts.map +1 -0
  34. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js +363 -0
  35. package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js.map +1 -0
  36. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  37. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +96 -79
  38. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  39. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  40. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +133 -123
  41. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  42. package/dist/planmagnet/product/ProductControls.d.ts +33 -0
  43. package/dist/planmagnet/product/ProductControls.d.ts.map +1 -1
  44. package/dist/planmagnet/product/ProductControls.js +593 -268
  45. package/dist/planmagnet/product/ProductControls.js.map +1 -1
  46. package/dist/planmagnet/product/routes.d.ts +10 -0
  47. package/dist/planmagnet/product/routes.d.ts.map +1 -0
  48. package/dist/planmagnet/product/routes.js +6 -0
  49. package/dist/planmagnet/product/routes.js.map +1 -0
  50. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts +10 -0
  51. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  52. package/dist/planmagnet/roadmaps/RoadmapControls.js +235 -184
  53. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  54. package/package.json +1 -1
@@ -2,14 +2,15 @@ import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/Pla
2
2
  import { throwOnError as n } from "../utils/mutationResult.js";
3
3
  import { ProjectSelect as r, WorkItemSearchSelect as i } from "../controls/Pickers.js";
4
4
  import { parseIntField as a } from "../utils/formValidation.js";
5
- import { useState as o } from "react";
6
- import { Loader2 as s, Save as c, Star as l, X as u } from "lucide-react";
7
- import { gql as d } from "@apollo/client";
8
- import { useMutation as f } from "@apollo/client/react";
9
- import { jsx as p, jsxs as m } from "react/jsx-runtime";
10
- import { Button as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y, Input as b } from "@burdenoff/fe-libs/ui";
5
+ import { EMPTY_PRIORITIZATION_FORM as o, PrioritizationScoreFields as s, prioritizationScoresValid as c } from "../controls/PrioritizationControls.js";
6
+ import { useState as l } from "react";
7
+ import { Loader2 as u, Save as d, Star as f, X as p } from "lucide-react";
8
+ import { gql as m } from "@apollo/client";
9
+ import { useMutation as h } from "@apollo/client/react";
10
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
+ import { Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S, Input as C } from "@burdenoff/fe-libs/ui";
11
12
  //#region src/planmagnet/pages/ProductsNewFeaturePagePage.tsx
12
- var x = d`
13
+ var w = m`
13
14
  mutation CreateFeatureRequest($input: PlanmagnetCreateFeatureRequestInput!) {
14
15
  planmagnetCreateFeatureRequest(input: $input) {
15
16
  id
@@ -21,8 +22,8 @@ var x = d`
21
22
  }
22
23
  }
23
24
  `;
24
- function S() {
25
- let { workspaceId: d } = e(), S = t(), [C, w] = o({
25
+ function T() {
26
+ let { workspaceId: m } = e(), T = t(), [E, D] = l({
26
27
  title: "",
27
28
  description: "",
28
29
  status: "SUBMITTED",
@@ -31,96 +32,106 @@ function S() {
31
32
  tags: "",
32
33
  projectId: "",
33
34
  workItemId: ""
34
- }), [T, { loading: E }] = f(x, { refetchQueries: ["ListFeatureRequests"] }), [D, O] = o(null), k = a(C.businessValue, {
35
+ }), [O, k] = l(o), [A, { loading: j }] = h(w, { refetchQueries: ["ListFeatureRequests"] }), [M, N] = l(null), P = a(E.businessValue, {
35
36
  min: 0,
36
37
  max: 100
38
+ }), F = c(O), I = a(O.valueScore, {
39
+ min: 1,
40
+ max: 10
41
+ }), L = a(O.effortScore, {
42
+ min: 1,
43
+ max: 10
37
44
  });
38
- return /* @__PURE__ */ m("div", {
45
+ return /* @__PURE__ */ _("div", {
39
46
  className: "space-y-6 p-6 max-w-4xl",
40
47
  children: [
41
- /* @__PURE__ */ m("div", {
48
+ /* @__PURE__ */ _("div", {
42
49
  className: "flex items-center justify-between",
43
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
50
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
44
51
  className: "text-2xl font-semibold",
45
52
  children: "New Feature"
46
- }), /* @__PURE__ */ p("p", {
53
+ }), /* @__PURE__ */ g("p", {
47
54
  className: "text-sm text-muted-foreground",
48
55
  children: "Define a new feature request with business value metrics."
49
- })] }), /* @__PURE__ */ m("div", {
56
+ })] }), /* @__PURE__ */ _("div", {
50
57
  className: "flex gap-2",
51
- children: [/* @__PURE__ */ m(h, {
58
+ children: [/* @__PURE__ */ _(v, {
52
59
  variant: "outline",
53
60
  size: "sm",
54
- onClick: () => S("/products/features-page"),
55
- children: [/* @__PURE__ */ p(u, { className: "mr-1 h-4 w-4" }), "Cancel"]
56
- }), /* @__PURE__ */ m(h, {
61
+ onClick: () => T("/products/features-page"),
62
+ children: [/* @__PURE__ */ g(p, { className: "mr-1 h-4 w-4" }), "Cancel"]
63
+ }), /* @__PURE__ */ _(v, {
57
64
  size: "sm",
58
65
  onClick: async () => {
59
- if (!(!C.title.trim() || k.invalid)) {
60
- O(null);
66
+ if (!(!E.title.trim() || P.invalid || !F)) {
67
+ N(null);
61
68
  try {
62
- await n(T({ variables: { input: {
63
- title: C.title,
64
- description: C.description || void 0,
65
- status: C.status,
66
- businessValue: k.value,
67
- effortEstimate: C.effortEstimate,
68
- projectId: C.projectId || void 0,
69
- workItemId: C.workItemId || void 0,
70
- tags: C.tags ? C.tags.split(",").map((e) => e.trim()).filter(Boolean) : void 0
71
- } } })), S("/products/features-page");
69
+ await n(A({ variables: { input: {
70
+ title: E.title,
71
+ description: E.description || void 0,
72
+ status: E.status,
73
+ businessValue: P.value,
74
+ effortEstimate: E.effortEstimate,
75
+ projectId: E.projectId || void 0,
76
+ workItemId: E.workItemId || void 0,
77
+ tags: E.tags ? E.tags.split(",").map((e) => e.trim()).filter(Boolean) : void 0,
78
+ moscow: O.moscow || void 0,
79
+ kano: O.kano || void 0,
80
+ valueScore: I.value,
81
+ effortScore: L.value
82
+ } } })), T("/products/features-page");
72
83
  } catch (e) {
73
- O(e instanceof Error ? e.message : "The feature could not be created.");
84
+ N(e instanceof Error ? e.message : "The feature could not be created.");
74
85
  }
75
86
  }
76
87
  },
77
- disabled: E || !C.title.trim() || k.invalid,
78
- children: [E ? /* @__PURE__ */ p(s, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ p(c, { className: "mr-1 h-4 w-4" }), "Create Feature"]
88
+ disabled: j || !E.title.trim() || P.invalid || !F,
89
+ children: [j ? /* @__PURE__ */ g(u, { className: "mr-1 h-4 w-4 animate-spin" }) : /* @__PURE__ */ g(d, { className: "mr-1 h-4 w-4" }), "Create Feature"]
79
90
  })]
80
91
  })]
81
92
  }),
82
- D && /* @__PURE__ */ m("div", {
93
+ M && /* @__PURE__ */ _("div", {
83
94
  role: "alert",
84
95
  className: "p-3 rounded-md bg-destructive/10 text-destructive text-sm",
85
- children: ["Failed to create feature: ", D]
96
+ children: ["Failed to create feature: ", M]
86
97
  }),
87
- /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Feature Details" }) }), /* @__PURE__ */ m(_, {
98
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Feature Details" }) }), /* @__PURE__ */ _(b, {
88
99
  className: "space-y-4",
89
100
  children: [
90
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
101
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
91
102
  className: "text-sm font-medium",
92
103
  children: "Title"
93
- }), /* @__PURE__ */ p(b, {
94
- value: C.title,
95
- onChange: (e) => w({
96
- ...C,
104
+ }), /* @__PURE__ */ g(C, {
105
+ value: E.title,
106
+ onChange: (e) => D({
107
+ ...E,
97
108
  title: e.target.value
98
109
  }),
99
110
  className: "mt-1",
100
111
  placeholder: "e.g. Advanced Search Filters"
101
112
  })] }),
102
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
113
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
103
114
  className: "text-sm font-medium",
104
115
  children: "Description"
105
- }), /* @__PURE__ */ p("textarea", {
116
+ }), /* @__PURE__ */ g("textarea", {
106
117
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[100px]",
107
- value: C.description,
108
- onChange: (e) => w({
109
- ...C,
118
+ value: E.description,
119
+ onChange: (e) => D({
120
+ ...E,
110
121
  description: e.target.value
111
122
  }),
112
123
  placeholder: "Describe the feature and its user value..."
113
124
  })] }),
114
- /* @__PURE__ */ m("div", {
125
+ /* @__PURE__ */ _("div", {
115
126
  className: "grid grid-cols-2 gap-4",
116
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
127
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
117
128
  className: "text-sm font-medium",
118
129
  children: "Status"
119
- }), /* @__PURE__ */ p("select", {
130
+ }), /* @__PURE__ */ g("select", {
120
131
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize",
121
- value: C.status,
122
- onChange: (e) => w({
123
- ...C,
132
+ value: E.status,
133
+ onChange: (e) => D({
134
+ ...E,
124
135
  status: e.target.value
125
136
  }),
126
137
  children: [
@@ -129,18 +140,18 @@ function S() {
129
140
  "PLANNED",
130
141
  "IN_PROGRESS",
131
142
  "COMPLETED"
132
- ].map((e) => /* @__PURE__ */ p("option", {
143
+ ].map((e) => /* @__PURE__ */ g("option", {
133
144
  value: e,
134
145
  children: e.toLowerCase().replace("_", " ")
135
146
  }, e))
136
- })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
147
+ })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
137
148
  className: "text-sm font-medium",
138
149
  children: "Effort Estimate"
139
- }), /* @__PURE__ */ p("select", {
150
+ }), /* @__PURE__ */ g("select", {
140
151
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize",
141
- value: C.effortEstimate,
142
- onChange: (e) => w({
143
- ...C,
152
+ value: E.effortEstimate,
153
+ onChange: (e) => D({
154
+ ...E,
144
155
  effortEstimate: e.target.value
145
156
  }),
146
157
  children: [
@@ -148,54 +159,54 @@ function S() {
148
159
  "medium",
149
160
  "large",
150
161
  "epic"
151
- ].map((e) => /* @__PURE__ */ p("option", {
162
+ ].map((e) => /* @__PURE__ */ g("option", {
152
163
  value: e,
153
164
  children: e
154
165
  }, e))
155
166
  })] })]
156
167
  }),
157
- /* @__PURE__ */ m("div", {
168
+ /* @__PURE__ */ _("div", {
158
169
  className: "grid grid-cols-2 gap-4",
159
- children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
170
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
160
171
  htmlFor: "feature-project",
161
172
  className: "text-sm font-medium",
162
173
  children: "Project"
163
- }), /* @__PURE__ */ p("div", {
174
+ }), /* @__PURE__ */ g("div", {
164
175
  className: "mt-1",
165
- children: /* @__PURE__ */ p(r, {
176
+ children: /* @__PURE__ */ g(r, {
166
177
  id: "feature-project",
167
- value: C.projectId,
178
+ value: E.projectId,
168
179
  noneLabel: "No project",
169
- onChange: (e) => w({
170
- ...C,
180
+ onChange: (e) => D({
181
+ ...E,
171
182
  projectId: e,
172
183
  workItemId: ""
173
184
  })
174
185
  })
175
- })] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
186
+ })] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
176
187
  htmlFor: "feature-work-item",
177
188
  className: "text-sm font-medium",
178
189
  children: "Delivered by work item"
179
- }), /* @__PURE__ */ p("div", {
190
+ }), /* @__PURE__ */ g("div", {
180
191
  className: "mt-1",
181
- children: /* @__PURE__ */ p(i, {
192
+ children: /* @__PURE__ */ g(i, {
182
193
  id: "feature-work-item",
183
- projectId: C.projectId || void 0,
184
- value: C.workItemId,
185
- onChange: (e) => w({
186
- ...C,
194
+ projectId: E.projectId || void 0,
195
+ value: E.workItemId,
196
+ onChange: (e) => D({
197
+ ...E,
187
198
  workItemId: e
188
199
  })
189
200
  })
190
201
  })] })]
191
202
  }),
192
- /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
203
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
193
204
  className: "text-sm font-medium",
194
205
  children: "Tags (comma-separated)"
195
- }), /* @__PURE__ */ p(b, {
196
- value: C.tags,
197
- onChange: (e) => w({
198
- ...C,
206
+ }), /* @__PURE__ */ g(C, {
207
+ value: E.tags,
208
+ onChange: (e) => D({
209
+ ...E,
199
210
  tags: e.target.value
200
211
  }),
201
212
  className: "mt-1",
@@ -203,51 +214,56 @@ function S() {
203
214
  })] })
204
215
  ]
205
216
  })] }),
206
- /* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, {
217
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Prioritization" }) }), /* @__PURE__ */ g(b, { children: /* @__PURE__ */ g(s, {
218
+ idPrefix: "feature-new",
219
+ values: O,
220
+ onChange: k
221
+ }) })] }),
222
+ /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ _(S, {
207
223
  className: "flex items-center gap-2",
208
- children: [/* @__PURE__ */ p(l, { className: "h-4 w-4" }), "Business Value"]
209
- }) }), /* @__PURE__ */ m(_, {
224
+ children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), "Business Value"]
225
+ }) }), /* @__PURE__ */ _(b, {
210
226
  className: "space-y-4",
211
- children: [/* @__PURE__ */ m("div", { children: [
212
- /* @__PURE__ */ p("label", {
227
+ children: [/* @__PURE__ */ _("div", { children: [
228
+ /* @__PURE__ */ g("label", {
213
229
  htmlFor: "feature-business-value",
214
230
  className: "text-sm font-medium",
215
231
  children: "Business Value (0-100)"
216
232
  }),
217
- /* @__PURE__ */ p(b, {
233
+ /* @__PURE__ */ g(C, {
218
234
  id: "feature-business-value",
219
235
  type: "number",
220
236
  min: 0,
221
237
  max: 100,
222
238
  step: 1,
223
- value: C.businessValue,
224
- onChange: (e) => w({
225
- ...C,
239
+ value: E.businessValue,
240
+ onChange: (e) => D({
241
+ ...E,
226
242
  businessValue: e.target.value
227
243
  }),
228
- "aria-invalid": k.invalid,
244
+ "aria-invalid": P.invalid,
229
245
  className: "mt-1"
230
246
  }),
231
- k.invalid ? /* @__PURE__ */ p("p", {
247
+ P.invalid ? /* @__PURE__ */ g("p", {
232
248
  role: "alert",
233
249
  className: "text-xs text-destructive mt-1",
234
250
  children: "Business value must be a whole number from 0 to 100."
235
- }) : /* @__PURE__ */ p("p", {
251
+ }) : /* @__PURE__ */ g("p", {
236
252
  className: "text-xs text-muted-foreground mt-1",
237
253
  children: "How much business value does this feature deliver?"
238
254
  })
239
- ] }), /* @__PURE__ */ m("div", {
255
+ ] }), /* @__PURE__ */ _("div", {
240
256
  className: "p-4 bg-muted rounded-md text-center",
241
257
  children: [
242
- /* @__PURE__ */ p("p", {
258
+ /* @__PURE__ */ g("p", {
243
259
  className: "text-xs text-muted-foreground",
244
260
  children: "Business Value Score"
245
261
  }),
246
- /* @__PURE__ */ p("p", {
262
+ /* @__PURE__ */ g("p", {
247
263
  className: "text-3xl font-bold text-primary mt-2",
248
- children: k.invalid || k.value === void 0 ? "—" : k.value
264
+ children: P.invalid || P.value === void 0 ? "—" : P.value
249
265
  }),
250
- /* @__PURE__ */ p("p", {
266
+ /* @__PURE__ */ g("p", {
251
267
  className: "text-xs text-muted-foreground",
252
268
  children: "out of 100"
253
269
  })
@@ -258,6 +274,6 @@ function S() {
258
274
  });
259
275
  }
260
276
  //#endregion
261
- export { S as ProductsNewFeaturePagePage };
277
+ export { T as ProductsNewFeaturePagePage };
262
278
 
263
279
  //# sourceMappingURL=ProductsNewFeaturePagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsNewFeaturePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsNewFeaturePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Star, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { parseIntField } from '../utils/formValidation';\nimport { ProjectSelect, WorkItemSearchSelect } from '../controls/Pickers';\n\nconst CREATE_FEATURE_REQUEST = gql`\n mutation CreateFeatureRequest($input: PlanmagnetCreateFeatureRequestInput!) {\n planmagnetCreateFeatureRequest(input: $input) {\n id\n title\n status\n businessValue\n effortEstimate\n priorityScore\n }\n }\n`;\n\nexport function ProductsNewFeaturePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n title: '',\n description: '',\n status: 'SUBMITTED' as string,\n businessValue: '50',\n effortEstimate: 'medium',\n tags: '',\n projectId: '',\n workItemId: '',\n });\n\n // Navigate only once the service has accepted the feature: with fe-libs' errorPolicy 'all' a refused\n // mutation resolves, so the old onCompleted navigation could leave the form as if it had saved.\n const [createFeature, { loading: saving }] = useMutation(CREATE_FEATURE_REQUEST, {\n refetchQueries: ['ListFeatureRequests'],\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n // Business value is a whole number from 0 to 100 (BOFF-7833: prod had stored 150).\n const businessValue = parseIntField(form.businessValue, { min: 0, max: 100 });\n\n const statusOptions = [\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n ] as const;\n const efforts = ['small', 'medium', 'large', 'epic'] as const;\n\n const handleCreate = async () => {\n if (!form.title.trim() || businessValue.invalid) return;\n setSaveError(null);\n try {\n await throwOnError(\n createFeature({\n variables: {\n input: {\n title: form.title,\n description: form.description || undefined,\n status: form.status,\n businessValue: businessValue.value,\n effortEstimate: form.effortEstimate,\n projectId: form.projectId || undefined,\n workItemId: form.workItemId || undefined,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean)\n : undefined,\n },\n },\n })\n );\n navigate('/products/features-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The feature could not be created.');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">New Feature</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new feature request with business value metrics.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/features-page')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={saving || !form.title.trim() || businessValue.invalid}\n >\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Feature\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create feature: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Advanced Search Filters\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the feature and its user value...\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statusOptions.map((s) => (\n <option key={s} value={s}>\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Effort Estimate</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.effortEstimate}\n onChange={(e) => setForm({ ...form, effortEstimate: e.target.value })}\n >\n {efforts.map((e) => (\n <option key={e} value={e}>\n {e}\n </option>\n ))}\n </select>\n </div>\n </div>\n {/* BOFF-7966: a request could not be tied to a project or to the work item that delivers it, so the\n release readiness card's \"customer requests riding on unfinished work\" could never fill. */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"feature-project\" className=\"text-sm font-medium\">\n Project\n </label>\n <div className=\"mt-1\">\n <ProjectSelect\n id=\"feature-project\"\n value={form.projectId}\n noneLabel=\"No project\"\n onChange={(projectId) => setForm({ ...form, projectId, workItemId: '' })}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"feature-work-item\" className=\"text-sm font-medium\">\n Delivered by work item\n </label>\n <div className=\"mt-1\">\n <WorkItemSearchSelect\n id=\"feature-work-item\"\n projectId={form.projectId || undefined}\n value={form.workItemId}\n onChange={(workItemId) => setForm({ ...form, workItemId })}\n />\n </div>\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. search, ux, performance\"\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Star className=\"h-4 w-4\" />\n Business Value\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label htmlFor=\"feature-business-value\" className=\"text-sm font-medium\">\n Business Value (0-100)\n </label>\n <Input\n id=\"feature-business-value\"\n type=\"number\"\n min={0}\n max={100}\n step={1}\n value={form.businessValue}\n onChange={(e) => setForm({ ...form, businessValue: e.target.value })}\n aria-invalid={businessValue.invalid}\n className=\"mt-1\"\n />\n {businessValue.invalid ? (\n <p role=\"alert\" className=\"text-xs text-destructive mt-1\">\n Business value must be a whole number from 0 to 100.\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground mt-1\">\n How much business value does this feature deliver?\n </p>\n )}\n </div>\n\n <div className=\"p-4 bg-muted rounded-md text-center\">\n <p className=\"text-xs text-muted-foreground\">Business Value Score</p>\n <p className=\"text-3xl font-bold text-primary mt-2\">\n {businessValue.invalid || businessValue.value === undefined\n ? '—'\n : businessValue.value}\n </p>\n <p className=\"text-xs text-muted-foreground\">out of 100</p>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsNewFeaturePagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsNewFeaturePagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, X, Star, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { parseIntField } from '../utils/formValidation';\nimport { ProjectSelect, WorkItemSearchSelect } from '../controls/Pickers';\nimport {\n EMPTY_PRIORITIZATION_FORM,\n PrioritizationScoreFields,\n prioritizationScoresValid,\n type PrioritizationFormValues,\n} from '../controls/PrioritizationControls';\n\nconst CREATE_FEATURE_REQUEST = gql`\n mutation CreateFeatureRequest($input: PlanmagnetCreateFeatureRequestInput!) {\n planmagnetCreateFeatureRequest(input: $input) {\n id\n title\n status\n businessValue\n effortEstimate\n priorityScore\n }\n }\n`;\n\nexport function ProductsNewFeaturePagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n\n const [form, setForm] = useState({\n title: '',\n description: '',\n status: 'SUBMITTED' as string,\n businessValue: '50',\n effortEstimate: 'medium',\n tags: '',\n projectId: '',\n workItemId: '',\n });\n // PRM-27: MoSCoW/Kano/value/effort scoring, set at creation just like at edit time.\n const [prioritization, setPrioritization] =\n useState<PrioritizationFormValues>(EMPTY_PRIORITIZATION_FORM);\n\n // Navigate only once the service has accepted the feature: with fe-libs' errorPolicy 'all' a refused\n // mutation resolves, so the old onCompleted navigation could leave the form as if it had saved.\n const [createFeature, { loading: saving }] = useMutation(CREATE_FEATURE_REQUEST, {\n refetchQueries: ['ListFeatureRequests'],\n });\n const [saveError, setSaveError] = useState<string | null>(null);\n // Business value is a whole number from 0 to 100 (BOFF-7833: prod had stored 150).\n const businessValue = parseIntField(form.businessValue, { min: 0, max: 100 });\n const prioritizationValid = prioritizationScoresValid(prioritization);\n const value = parseIntField(prioritization.valueScore, { min: 1, max: 10 });\n const effort = parseIntField(prioritization.effortScore, { min: 1, max: 10 });\n\n const statusOptions = [\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n ] as const;\n const efforts = ['small', 'medium', 'large', 'epic'] as const;\n\n const handleCreate = async () => {\n if (!form.title.trim() || businessValue.invalid || !prioritizationValid) return;\n setSaveError(null);\n try {\n await throwOnError(\n createFeature({\n variables: {\n input: {\n title: form.title,\n description: form.description || undefined,\n status: form.status,\n businessValue: businessValue.value,\n effortEstimate: form.effortEstimate,\n projectId: form.projectId || undefined,\n workItemId: form.workItemId || undefined,\n tags: form.tags\n ? form.tags\n .split(',')\n .map((t) => t.trim())\n .filter(Boolean)\n : undefined,\n moscow: prioritization.moscow || undefined,\n kano: prioritization.kano || undefined,\n valueScore: value.value,\n effortScore: effort.value,\n },\n },\n })\n );\n navigate('/products/features-page');\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The feature could not be created.');\n }\n };\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">New Feature</h1>\n <p className=\"text-sm text-muted-foreground\">\n Define a new feature request with business value metrics.\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => navigate('/products/features-page')}>\n <X className=\"mr-1 h-4 w-4\" />\n Cancel\n </Button>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={saving || !form.title.trim() || businessValue.invalid || !prioritizationValid}\n >\n {saving ? (\n <Loader2 className=\"mr-1 h-4 w-4 animate-spin\" />\n ) : (\n <Save className=\"mr-1 h-4 w-4\" />\n )}\n Create Feature\n </Button>\n </div>\n </div>\n\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md bg-destructive/10 text-destructive text-sm\">\n Failed to create feature: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Feature Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Title</label>\n <Input\n value={form.title}\n onChange={(e) => setForm({ ...form, title: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. Advanced Search Filters\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">Description</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[100px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n placeholder=\"Describe the feature and its user value...\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Status</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.status}\n onChange={(e) => setForm({ ...form, status: e.target.value })}\n >\n {statusOptions.map((s) => (\n <option key={s} value={s}>\n {s.toLowerCase().replace('_', ' ')}\n </option>\n ))}\n </select>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Effort Estimate</label>\n <select\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm capitalize\"\n value={form.effortEstimate}\n onChange={(e) => setForm({ ...form, effortEstimate: e.target.value })}\n >\n {efforts.map((e) => (\n <option key={e} value={e}>\n {e}\n </option>\n ))}\n </select>\n </div>\n </div>\n {/* BOFF-7966: a request could not be tied to a project or to the work item that delivers it, so the\n release readiness card's \"customer requests riding on unfinished work\" could never fill. */}\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"feature-project\" className=\"text-sm font-medium\">\n Project\n </label>\n <div className=\"mt-1\">\n <ProjectSelect\n id=\"feature-project\"\n value={form.projectId}\n noneLabel=\"No project\"\n onChange={(projectId) => setForm({ ...form, projectId, workItemId: '' })}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"feature-work-item\" className=\"text-sm font-medium\">\n Delivered by work item\n </label>\n <div className=\"mt-1\">\n <WorkItemSearchSelect\n id=\"feature-work-item\"\n projectId={form.projectId || undefined}\n value={form.workItemId}\n onChange={(workItemId) => setForm({ ...form, workItemId })}\n />\n </div>\n </div>\n </div>\n <div>\n <label className=\"text-sm font-medium\">Tags (comma-separated)</label>\n <Input\n value={form.tags}\n onChange={(e) => setForm({ ...form, tags: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. search, ux, performance\"\n />\n </div>\n </CardContent>\n </Card>\n\n {/* PRM-27: MoSCoW, Kano and Value-vs-Effort scoring — an alternative to the Business Value card below. */}\n <Card>\n <CardHeader>\n <CardTitle>Prioritization</CardTitle>\n </CardHeader>\n <CardContent>\n <PrioritizationScoreFields\n idPrefix=\"feature-new\"\n values={prioritization}\n onChange={setPrioritization}\n />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle className=\"flex items-center gap-2\">\n <Star className=\"h-4 w-4\" />\n Business Value\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label htmlFor=\"feature-business-value\" className=\"text-sm font-medium\">\n Business Value (0-100)\n </label>\n <Input\n id=\"feature-business-value\"\n type=\"number\"\n min={0}\n max={100}\n step={1}\n value={form.businessValue}\n onChange={(e) => setForm({ ...form, businessValue: e.target.value })}\n aria-invalid={businessValue.invalid}\n className=\"mt-1\"\n />\n {businessValue.invalid ? (\n <p role=\"alert\" className=\"text-xs text-destructive mt-1\">\n Business value must be a whole number from 0 to 100.\n </p>\n ) : (\n <p className=\"text-xs text-muted-foreground mt-1\">\n How much business value does this feature deliver?\n </p>\n )}\n </div>\n\n <div className=\"p-4 bg-muted rounded-md text-center\">\n <p className=\"text-xs text-muted-foreground\">Business Value Score</p>\n <p className=\"text-3xl font-bold text-primary mt-2\">\n {businessValue.invalid || businessValue.value === undefined\n ? '—'\n : businessValue.value}\n </p>\n <p className=\"text-xs text-muted-foreground\">out of 100</p>\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function ProductsPrioritizationPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ProductsPrioritizationPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProductsPrioritizationPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsPrioritizationPagePage.tsx"],"names":[],"mappings":"AAyQA,wBAAgB,8BAA8B,4CA0M7C"}