@burdenoff/microfe-planmagnet 2026.930.23 → 2026.930.25
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/index.js +6 -5
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +154 -147
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +66 -62
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/comments/CommentThread.d.ts +8 -1
- package/dist/planmagnet/comments/CommentThread.d.ts.map +1 -1
- package/dist/planmagnet/comments/CommentThread.js +188 -181
- package/dist/planmagnet/comments/CommentThread.js.map +1 -1
- package/dist/planmagnet/comments/TargetComments.d.ts +10 -2
- package/dist/planmagnet/comments/TargetComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/TargetComments.js +10 -8
- package/dist/planmagnet/comments/TargetComments.js.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.d.ts +7 -1
- package/dist/planmagnet/comments/WorkItemComments.d.ts.map +1 -1
- package/dist/planmagnet/comments/WorkItemComments.js +5 -4
- package/dist/planmagnet/comments/WorkItemComments.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +11 -0
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +305 -135
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/Pickers.d.ts +18 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
- package/dist/planmagnet/controls/Pickers.js +78 -1
- package/dist/planmagnet/controls/Pickers.js.map +1 -1
- package/dist/planmagnet/controls/PrioritizationControls.d.ts +72 -0
- package/dist/planmagnet/controls/PrioritizationControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/PrioritizationControls.js +246 -0
- package/dist/planmagnet/controls/PrioritizationControls.js.map +1 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.d.ts +8 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.d.ts.map +1 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.js +217 -0
- package/dist/planmagnet/discussion/WorkItemDiscussionPanel.js.map +1 -0
- package/dist/planmagnet/docs/DocDiffView.d.ts +26 -0
- package/dist/planmagnet/docs/DocDiffView.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocDiffView.js +52 -0
- package/dist/planmagnet/docs/DocDiffView.js.map +1 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.d.ts +13 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.js +308 -0
- package/dist/planmagnet/docs/DocSuggestionsPanel.js.map +1 -0
- package/dist/planmagnet/docs/DocVersionCompare.d.ts +7 -0
- package/dist/planmagnet/docs/DocVersionCompare.d.ts.map +1 -0
- package/dist/planmagnet/docs/DocVersionCompare.js +105 -0
- package/dist/planmagnet/docs/DocVersionCompare.js.map +1 -0
- package/dist/planmagnet/hooks/usePresence.d.ts +39 -0
- package/dist/planmagnet/hooks/usePresence.d.ts.map +1 -0
- package/dist/planmagnet/hooks/usePresence.js +146 -0
- package/dist/planmagnet/hooks/usePresence.js.map +1 -0
- package/dist/planmagnet/index.d.ts +1 -0
- package/dist/planmagnet/index.d.ts.map +1 -1
- package/dist/planmagnet/index.js +1 -0
- package/dist/planmagnet/links/CodeLinksPanel.d.ts +22 -0
- package/dist/planmagnet/links/CodeLinksPanel.d.ts.map +1 -0
- package/dist/planmagnet/links/CodeLinksPanel.js +94 -0
- package/dist/planmagnet/links/CodeLinksPanel.js.map +1 -0
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +488 -217
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +131 -125
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +183 -99
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +113 -97
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js +363 -0
- package/dist/planmagnet/pages/ProductsPrioritizationPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +96 -79
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +144 -128
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +205 -196
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +194 -160
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/presence/PresenceAvatars.d.ts +16 -0
- package/dist/planmagnet/presence/PresenceAvatars.d.ts.map +1 -0
- package/dist/planmagnet/presence/PresenceAvatars.js +56 -0
- package/dist/planmagnet/presence/PresenceAvatars.js.map +1 -0
- package/dist/planmagnet/product/ProductControls.d.ts +33 -0
- package/dist/planmagnet/product/ProductControls.d.ts.map +1 -1
- package/dist/planmagnet/product/ProductControls.js +593 -268
- package/dist/planmagnet/product/ProductControls.js.map +1 -1
- package/dist/planmagnet/product/routes.d.ts +10 -0
- package/dist/planmagnet/product/routes.d.ts.map +1 -0
- package/dist/planmagnet/product/routes.js +6 -0
- package/dist/planmagnet/product/routes.js.map +1 -0
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts +10 -0
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +235 -184
- package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
- package/dist/planmagnet/utils/graphqlErrors.d.ts +6 -0
- package/dist/planmagnet/utils/graphqlErrors.d.ts.map +1 -1
- package/dist/planmagnet/utils/graphqlErrors.js +5 -2
- package/dist/planmagnet/utils/graphqlErrors.js.map +1 -1
- package/dist/planmagnet/utils/lineDiff.d.ts +21 -0
- package/dist/planmagnet/utils/lineDiff.d.ts.map +1 -0
- package/dist/planmagnet/utils/lineDiff.js +36 -0
- package/dist/planmagnet/utils/lineDiff.js.map +1 -0
- 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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
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
|
|
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
|
|
25
|
-
let { workspaceId:
|
|
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
|
-
}), [
|
|
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__ */
|
|
45
|
+
return /* @__PURE__ */ _("div", {
|
|
39
46
|
className: "space-y-6 p-6 max-w-4xl",
|
|
40
47
|
children: [
|
|
41
|
-
/* @__PURE__ */
|
|
48
|
+
/* @__PURE__ */ _("div", {
|
|
42
49
|
className: "flex items-center justify-between",
|
|
43
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("h1", {
|
|
44
51
|
className: "text-2xl font-semibold",
|
|
45
52
|
children: "New Feature"
|
|
46
|
-
}), /* @__PURE__ */
|
|
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__ */
|
|
56
|
+
})] }), /* @__PURE__ */ _("div", {
|
|
50
57
|
className: "flex gap-2",
|
|
51
|
-
children: [/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ _(v, {
|
|
52
59
|
variant: "outline",
|
|
53
60
|
size: "sm",
|
|
54
|
-
onClick: () =>
|
|
55
|
-
children: [/* @__PURE__ */ p
|
|
56
|
-
}), /* @__PURE__ */
|
|
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 (!(!
|
|
60
|
-
|
|
66
|
+
if (!(!E.title.trim() || P.invalid || !F)) {
|
|
67
|
+
N(null);
|
|
61
68
|
try {
|
|
62
|
-
await n(
|
|
63
|
-
title:
|
|
64
|
-
description:
|
|
65
|
-
status:
|
|
66
|
-
businessValue:
|
|
67
|
-
effortEstimate:
|
|
68
|
-
projectId:
|
|
69
|
-
workItemId:
|
|
70
|
-
tags:
|
|
71
|
-
|
|
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
|
-
|
|
84
|
+
N(e instanceof Error ? e.message : "The feature could not be created.");
|
|
74
85
|
}
|
|
75
86
|
}
|
|
76
87
|
},
|
|
77
|
-
disabled:
|
|
78
|
-
children: [
|
|
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
|
-
|
|
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: ",
|
|
96
|
+
children: ["Failed to create feature: ", M]
|
|
86
97
|
}),
|
|
87
|
-
/* @__PURE__ */
|
|
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__ */
|
|
101
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
91
102
|
className: "text-sm font-medium",
|
|
92
103
|
children: "Title"
|
|
93
|
-
}), /* @__PURE__ */
|
|
94
|
-
value:
|
|
95
|
-
onChange: (e) =>
|
|
96
|
-
...
|
|
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__ */
|
|
113
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
103
114
|
className: "text-sm font-medium",
|
|
104
115
|
children: "Description"
|
|
105
|
-
}), /* @__PURE__ */
|
|
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:
|
|
108
|
-
onChange: (e) =>
|
|
109
|
-
...
|
|
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__ */
|
|
125
|
+
/* @__PURE__ */ _("div", {
|
|
115
126
|
className: "grid grid-cols-2 gap-4",
|
|
116
|
-
children: [/* @__PURE__ */
|
|
127
|
+
children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
117
128
|
className: "text-sm font-medium",
|
|
118
129
|
children: "Status"
|
|
119
|
-
}), /* @__PURE__ */
|
|
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:
|
|
122
|
-
onChange: (e) =>
|
|
123
|
-
...
|
|
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__ */
|
|
143
|
+
].map((e) => /* @__PURE__ */ g("option", {
|
|
133
144
|
value: e,
|
|
134
145
|
children: e.toLowerCase().replace("_", " ")
|
|
135
146
|
}, e))
|
|
136
|
-
})] }), /* @__PURE__ */
|
|
147
|
+
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
137
148
|
className: "text-sm font-medium",
|
|
138
149
|
children: "Effort Estimate"
|
|
139
|
-
}), /* @__PURE__ */
|
|
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:
|
|
142
|
-
onChange: (e) =>
|
|
143
|
-
...
|
|
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__ */
|
|
162
|
+
].map((e) => /* @__PURE__ */ g("option", {
|
|
152
163
|
value: e,
|
|
153
164
|
children: e
|
|
154
165
|
}, e))
|
|
155
166
|
})] })]
|
|
156
167
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ _("div", {
|
|
158
169
|
className: "grid grid-cols-2 gap-4",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
174
|
+
}), /* @__PURE__ */ g("div", {
|
|
164
175
|
className: "mt-1",
|
|
165
|
-
children: /* @__PURE__ */
|
|
176
|
+
children: /* @__PURE__ */ g(r, {
|
|
166
177
|
id: "feature-project",
|
|
167
|
-
value:
|
|
178
|
+
value: E.projectId,
|
|
168
179
|
noneLabel: "No project",
|
|
169
|
-
onChange: (e) =>
|
|
170
|
-
...
|
|
180
|
+
onChange: (e) => D({
|
|
181
|
+
...E,
|
|
171
182
|
projectId: e,
|
|
172
183
|
workItemId: ""
|
|
173
184
|
})
|
|
174
185
|
})
|
|
175
|
-
})] }), /* @__PURE__ */
|
|
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__ */
|
|
190
|
+
}), /* @__PURE__ */ g("div", {
|
|
180
191
|
className: "mt-1",
|
|
181
|
-
children: /* @__PURE__ */
|
|
192
|
+
children: /* @__PURE__ */ g(i, {
|
|
182
193
|
id: "feature-work-item",
|
|
183
|
-
projectId:
|
|
184
|
-
value:
|
|
185
|
-
onChange: (e) =>
|
|
186
|
-
...
|
|
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__ */
|
|
203
|
+
/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
193
204
|
className: "text-sm font-medium",
|
|
194
205
|
children: "Tags (comma-separated)"
|
|
195
|
-
}), /* @__PURE__ */
|
|
196
|
-
value:
|
|
197
|
-
onChange: (e) =>
|
|
198
|
-
...
|
|
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__ */
|
|
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__ */
|
|
209
|
-
}) }), /* @__PURE__ */
|
|
224
|
+
children: [/* @__PURE__ */ g(f, { className: "h-4 w-4" }), "Business Value"]
|
|
225
|
+
}) }), /* @__PURE__ */ _(b, {
|
|
210
226
|
className: "space-y-4",
|
|
211
|
-
children: [/* @__PURE__ */
|
|
212
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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:
|
|
224
|
-
onChange: (e) =>
|
|
225
|
-
...
|
|
239
|
+
value: E.businessValue,
|
|
240
|
+
onChange: (e) => D({
|
|
241
|
+
...E,
|
|
226
242
|
businessValue: e.target.value
|
|
227
243
|
}),
|
|
228
|
-
"aria-invalid":
|
|
244
|
+
"aria-invalid": P.invalid,
|
|
229
245
|
className: "mt-1"
|
|
230
246
|
}),
|
|
231
|
-
|
|
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__ */
|
|
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__ */
|
|
255
|
+
] }), /* @__PURE__ */ _("div", {
|
|
240
256
|
className: "p-4 bg-muted rounded-md text-center",
|
|
241
257
|
children: [
|
|
242
|
-
/* @__PURE__ */
|
|
258
|
+
/* @__PURE__ */ g("p", {
|
|
243
259
|
className: "text-xs text-muted-foreground",
|
|
244
260
|
children: "Business Value Score"
|
|
245
261
|
}),
|
|
246
|
-
/* @__PURE__ */
|
|
262
|
+
/* @__PURE__ */ g("p", {
|
|
247
263
|
className: "text-3xl font-bold text-primary mt-2",
|
|
248
|
-
children:
|
|
264
|
+
children: P.invalid || P.value === void 0 ? "—" : P.value
|
|
249
265
|
}),
|
|
250
|
-
/* @__PURE__ */
|
|
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 {
|
|
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 @@
|
|
|
1
|
+
{"version":3,"file":"ProductsPrioritizationPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsPrioritizationPagePage.tsx"],"names":[],"mappings":"AAyQA,wBAAgB,8BAA8B,4CA0M7C"}
|