@burdenoff/microfe-planmagnet 2026.1001.8 → 2026.1002.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/dist/generated/global-types.d.ts +5 -0
  2. package/dist/generated/global-types.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.d.ts +1814 -401
  4. package/dist/generated/wspace-operations.d.ts.map +1 -1
  5. package/dist/generated/wspace-types.d.ts +5167 -54
  6. package/dist/generated/wspace-types.d.ts.map +1 -1
  7. package/dist/index.js +8 -8
  8. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  9. package/dist/planmagnet/PlanMagnetRoot.js +172 -160
  10. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  11. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  12. package/dist/planmagnet/PlanMagnetRoutes.js +95 -83
  13. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  14. package/dist/planmagnet/comments/CommentAttachmentUpload.d.ts +16 -0
  15. package/dist/planmagnet/comments/CommentAttachmentUpload.d.ts.map +1 -0
  16. package/dist/planmagnet/comments/CommentAttachmentUpload.js +218 -0
  17. package/dist/planmagnet/comments/CommentAttachmentUpload.js.map +1 -0
  18. package/dist/planmagnet/comments/CommentAttachments.d.ts +15 -0
  19. package/dist/planmagnet/comments/CommentAttachments.d.ts.map +1 -0
  20. package/dist/planmagnet/comments/CommentAttachments.js +74 -0
  21. package/dist/planmagnet/comments/CommentAttachments.js.map +1 -0
  22. package/dist/planmagnet/comments/CommentBody.d.ts +11 -0
  23. package/dist/planmagnet/comments/CommentBody.d.ts.map +1 -0
  24. package/dist/planmagnet/comments/CommentBody.js +130 -0
  25. package/dist/planmagnet/comments/CommentBody.js.map +1 -0
  26. package/dist/planmagnet/comments/CommentThread.d.ts +7 -2
  27. package/dist/planmagnet/comments/CommentThread.d.ts.map +1 -1
  28. package/dist/planmagnet/comments/CommentThread.js +288 -233
  29. package/dist/planmagnet/comments/CommentThread.js.map +1 -1
  30. package/dist/planmagnet/comments/MentionComposer.d.ts +0 -7
  31. package/dist/planmagnet/comments/MentionComposer.d.ts.map +1 -1
  32. package/dist/planmagnet/comments/MentionComposer.js +225 -86
  33. package/dist/planmagnet/comments/MentionComposer.js.map +1 -1
  34. package/dist/planmagnet/comments/TargetComments.d.ts.map +1 -1
  35. package/dist/planmagnet/comments/TargetComments.js +44 -6
  36. package/dist/planmagnet/comments/TargetComments.js.map +1 -1
  37. package/dist/planmagnet/comments/WorkItemComments.d.ts.map +1 -1
  38. package/dist/planmagnet/comments/WorkItemComments.js +22 -3
  39. package/dist/planmagnet/comments/WorkItemComments.js.map +1 -1
  40. package/dist/planmagnet/comments/commentAttachmentUploadClient.d.ts +102 -0
  41. package/dist/planmagnet/comments/commentAttachmentUploadClient.d.ts.map +1 -0
  42. package/dist/planmagnet/comments/commentAttachmentUploadClient.js +298 -0
  43. package/dist/planmagnet/comments/commentAttachmentUploadClient.js.map +1 -0
  44. package/dist/planmagnet/comments/commentImageTokens.d.ts +5 -0
  45. package/dist/planmagnet/comments/commentImageTokens.d.ts.map +1 -0
  46. package/dist/planmagnet/comments/commentImageTokens.js +14 -0
  47. package/dist/planmagnet/comments/commentImageTokens.js.map +1 -0
  48. package/dist/planmagnet/comments/commentTokens.d.ts +31 -0
  49. package/dist/planmagnet/comments/commentTokens.d.ts.map +1 -0
  50. package/dist/planmagnet/comments/commentTokens.js +142 -0
  51. package/dist/planmagnet/comments/commentTokens.js.map +1 -0
  52. package/dist/planmagnet/comments/fileDisplay.d.ts +22 -0
  53. package/dist/planmagnet/comments/fileDisplay.d.ts.map +1 -0
  54. package/dist/planmagnet/comments/fileDisplay.js +48 -0
  55. package/dist/planmagnet/comments/fileDisplay.js.map +1 -0
  56. package/dist/planmagnet/comments/useCommentFileUrl.d.ts +22 -0
  57. package/dist/planmagnet/comments/useCommentFileUrl.d.ts.map +1 -0
  58. package/dist/planmagnet/comments/useCommentFileUrl.js +54 -0
  59. package/dist/planmagnet/comments/useCommentFileUrl.js.map +1 -0
  60. package/dist/planmagnet/controls/Pickers.d.ts +7 -2
  61. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  62. package/dist/planmagnet/controls/Pickers.js +10 -9
  63. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  64. package/dist/planmagnet/index.d.ts +1 -1
  65. package/dist/planmagnet/index.d.ts.map +1 -1
  66. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +4 -4
  67. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  68. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  69. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +133 -131
  70. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  71. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  72. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +5 -5
  73. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  74. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +6 -6
  75. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  76. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  77. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +105 -82
  78. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  79. package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.d.ts +2 -0
  80. package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.d.ts.map +1 -0
  81. package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.js +174 -0
  82. package/dist/planmagnet/pages/ProductsProductFeatureDetailPagePage.js.map +1 -0
  83. package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.d.ts +2 -0
  84. package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.d.ts.map +1 -0
  85. package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.js +350 -0
  86. package/dist/planmagnet/pages/ProductsProductHierarchyPagePage.js.map +1 -0
  87. package/dist/planmagnet/pages/ProductsProductLinesPagePage.d.ts +2 -0
  88. package/dist/planmagnet/pages/ProductsProductLinesPagePage.d.ts.map +1 -0
  89. package/dist/planmagnet/pages/ProductsProductLinesPagePage.js +168 -0
  90. package/dist/planmagnet/pages/ProductsProductLinesPagePage.js.map +1 -0
  91. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  92. package/dist/planmagnet/pages/ProductsProductsPagePage.js +1 -1
  93. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  94. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  95. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +156 -154
  96. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  97. package/dist/planmagnet/product/FeatureLinkPanels.d.ts +29 -0
  98. package/dist/planmagnet/product/FeatureLinkPanels.d.ts.map +1 -0
  99. package/dist/planmagnet/product/FeatureLinkPanels.js +373 -0
  100. package/dist/planmagnet/product/FeatureLinkPanels.js.map +1 -0
  101. package/dist/planmagnet/product/ProductControls.js +1 -1
  102. package/dist/planmagnet/product/ProductControls.js.map +1 -1
  103. package/dist/planmagnet/product/ProductFeatureControls.d.ts +39 -0
  104. package/dist/planmagnet/product/ProductFeatureControls.d.ts.map +1 -0
  105. package/dist/planmagnet/product/ProductFeatureControls.js +315 -0
  106. package/dist/planmagnet/product/ProductFeatureControls.js.map +1 -0
  107. package/dist/planmagnet/product/ProductFeatureLinkCard.d.ts +4 -0
  108. package/dist/planmagnet/product/ProductFeatureLinkCard.d.ts.map +1 -0
  109. package/dist/planmagnet/product/ProductFeatureLinkCard.js +192 -0
  110. package/dist/planmagnet/product/ProductFeatureLinkCard.js.map +1 -0
  111. package/dist/planmagnet/product/ProductLineControls.d.ts +52 -0
  112. package/dist/planmagnet/product/ProductLineControls.d.ts.map +1 -0
  113. package/dist/planmagnet/product/ProductLineControls.js +417 -0
  114. package/dist/planmagnet/product/ProductLineControls.js.map +1 -0
  115. package/dist/planmagnet/product/ProductTeamsPanel.d.ts +4 -0
  116. package/dist/planmagnet/product/ProductTeamsPanel.d.ts.map +1 -0
  117. package/dist/planmagnet/product/ProductTeamsPanel.js +129 -0
  118. package/dist/planmagnet/product/ProductTeamsPanel.js.map +1 -0
  119. package/dist/planmagnet/product/WorkItemProductFeatureChip.d.ts +4 -0
  120. package/dist/planmagnet/product/WorkItemProductFeatureChip.d.ts.map +1 -0
  121. package/dist/planmagnet/product/WorkItemProductFeatureChip.js +41 -0
  122. package/dist/planmagnet/product/WorkItemProductFeatureChip.js.map +1 -0
  123. package/dist/planmagnet/product/hierarchy.d.ts +127 -0
  124. package/dist/planmagnet/product/hierarchy.d.ts.map +1 -0
  125. package/dist/planmagnet/product/hierarchy.js +173 -0
  126. package/dist/planmagnet/product/hierarchy.js.map +1 -0
  127. package/dist/planmagnet/product/hierarchyOperations.d.ts +35 -0
  128. package/dist/planmagnet/product/hierarchyOperations.d.ts.map +1 -0
  129. package/dist/planmagnet/product/hierarchyOperations.js +440 -0
  130. package/dist/planmagnet/product/hierarchyOperations.js.map +1 -0
  131. package/dist/planmagnet/product/refusal.d.ts +14 -0
  132. package/dist/planmagnet/product/refusal.d.ts.map +1 -0
  133. package/dist/planmagnet/product/refusal.js +10 -0
  134. package/dist/planmagnet/product/refusal.js.map +1 -0
  135. package/dist/planmagnet/product/routes.d.ts +4 -0
  136. package/dist/planmagnet/product/routes.d.ts.map +1 -1
  137. package/dist/planmagnet/product/routes.js +2 -2
  138. package/dist/planmagnet/product/routes.js.map +1 -1
  139. package/dist/shared/utils/i18n.d.ts +3 -0
  140. package/dist/shared/utils/i18n.d.ts.map +1 -1
  141. package/dist/shared/utils/i18n.js +1 -1
  142. package/dist/shared/utils/i18n.js.map +1 -1
  143. package/package.json +5 -4
@@ -6,15 +6,16 @@ import { FeatureTriageActions as a, isOpenRequest as o, isSnoozed as s } from ".
6
6
  import { CategoryBadge as c, RatingDisplay as l } from "../controls/FeedbackControls.js";
7
7
  import { FeatureHeaderActions as u, FeaturePrioritizationCard as d, FeatureVoteButton as f } from "../controls/FeatureControls.js";
8
8
  import { FeatureInitiativeCard as p } from "../roadmaps/FeatureInitiativeCard.js";
9
- import { useState as m } from "react";
10
- import { Loader2 as h, MessageSquare as g, Star as _, Target as v, ThumbsUp as y } from "lucide-react";
11
- import { useParams as b } from "react-router";
12
- import { gql as x } from "@apollo/client";
13
- import { useQuery as S } from "@apollo/client/react";
14
- import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
15
- import { Badge as E, Card as D, CardContent as O, CardHeader as k, CardTitle as A } from "@burdenoff/fe-libs/ui";
9
+ import { ProductFeatureLinkCard as m } from "../product/ProductFeatureLinkCard.js";
10
+ import { useState as h } from "react";
11
+ import { Loader2 as g, MessageSquare as _, Star as v, Target as y, ThumbsUp as b } from "lucide-react";
12
+ import { useParams as x } from "react-router";
13
+ import { gql as S } from "@apollo/client";
14
+ import { useQuery as C } from "@apollo/client/react";
15
+ import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
16
+ import { Badge as D, Card as O, CardContent as k, CardHeader as A, CardTitle as j } from "@burdenoff/fe-libs/ui";
16
17
  //#region src/planmagnet/pages/ProductsFeatureDetailPagePage.tsx
17
- var j = x`
18
+ var M = S`
18
19
  query GetFeatureRequest($id: ID!) {
19
20
  planmagnetFeatureRequest(id: $id) {
20
21
  id
@@ -78,88 +79,88 @@ var j = x`
78
79
  }
79
80
  }
80
81
  `;
81
- function M() {
82
- let { workspaceId: x, currentUser: M } = e(), N = t(), [P] = m(() => Date.now()), F = b().id ?? "", { data: I, loading: L, error: R } = S(j, {
83
- variables: { id: F },
84
- skip: !F
82
+ function N() {
83
+ let { workspaceId: S, currentUser: N } = e(), P = t(), [F] = h(() => Date.now()), I = x().id ?? "", { data: L, loading: R, error: z } = C(M, {
84
+ variables: { id: I },
85
+ skip: !I
85
86
  });
86
- if (L && !I) return /* @__PURE__ */ w("div", {
87
+ if (R && !L) return /* @__PURE__ */ T("div", {
87
88
  className: "flex items-center justify-center p-12",
88
- children: /* @__PURE__ */ w(h, { className: "h-8 w-8 animate-spin text-muted-foreground" })
89
+ children: /* @__PURE__ */ T(g, { className: "h-8 w-8 animate-spin text-muted-foreground" })
89
90
  });
90
- if (R) return /* @__PURE__ */ T("div", {
91
+ if (z) return /* @__PURE__ */ E("div", {
91
92
  className: "p-6 text-destructive",
92
- children: ["Failed to load feature: ", R.message]
93
+ children: ["Failed to load feature request: ", z.message]
93
94
  });
94
- let z = I?.planmagnetFeatureRequest;
95
- if (!z) return /* @__PURE__ */ w("div", {
95
+ let B = L?.planmagnetFeatureRequest;
96
+ if (!B) return /* @__PURE__ */ T("div", {
96
97
  className: "p-6 text-muted-foreground",
97
- children: "Feature not found."
98
+ children: "Feature request not found."
98
99
  });
99
- let B = z.votes ?? [], V = z.competitors ?? [];
100
- return /* @__PURE__ */ T("div", {
100
+ let V = B.votes ?? [], H = B.competitors ?? [];
101
+ return /* @__PURE__ */ E("div", {
101
102
  className: "space-y-6 p-6",
102
103
  children: [
103
- /* @__PURE__ */ T("div", {
104
+ /* @__PURE__ */ E("div", {
104
105
  className: "flex items-center justify-between",
105
- children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("h1", {
106
+ children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T("h1", {
106
107
  className: "text-2xl font-semibold",
107
- children: z.title
108
- }), /* @__PURE__ */ T("p", {
108
+ children: B.title
109
+ }), /* @__PURE__ */ E("p", {
109
110
  className: "text-sm text-muted-foreground",
110
111
  children: [
111
112
  "Submitted by: ",
112
- /* @__PURE__ */ w(n, { id: z.submittedBy }),
113
+ /* @__PURE__ */ T(n, { id: B.submittedBy }),
113
114
  " · Effort:",
114
115
  " ",
115
- z.effortEstimate ?? "N/A"
116
+ B.effortEstimate ?? "N/A"
116
117
  ]
117
- })] }), /* @__PURE__ */ T("div", {
118
+ })] }), /* @__PURE__ */ E("div", {
118
119
  className: "flex flex-col items-end gap-2",
119
- children: [/* @__PURE__ */ w(f, {
120
- featureId: z.id,
121
- voted: B.some((e) => e.voterId === M?.id)
122
- }, z.id), /* @__PURE__ */ w(u, { feature: z })]
120
+ children: [/* @__PURE__ */ T(f, {
121
+ featureId: B.id,
122
+ voted: V.some((e) => e.voterId === N?.id)
123
+ }, B.id), /* @__PURE__ */ T(u, { feature: B })]
123
124
  })]
124
125
  }),
125
- o(z.status) && /* @__PURE__ */ T(D, {
126
+ o(B.status) && /* @__PURE__ */ E(O, {
126
127
  "data-testid": "triage-card",
127
- children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Triage" }) }), /* @__PURE__ */ T(O, {
128
+ children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Triage" }) }), /* @__PURE__ */ E(k, {
128
129
  className: "space-y-2",
129
- children: [/* @__PURE__ */ w("p", {
130
+ children: [/* @__PURE__ */ T("p", {
130
131
  className: "text-sm text-muted-foreground",
131
- children: s(z.snoozedUntil, P) ? `Snoozed until ${new Date(z.snoozedUntil).toLocaleString()} — it returns to the triage queue then.` : z.status === "SUBMITTED" ? "Waiting for a decision." : "Under review: accept it as planned, decline it, or merge it into its original."
132
- }), /* @__PURE__ */ w(a, {
133
- feature: z,
134
- now: P
132
+ children: s(B.snoozedUntil, F) ? `Snoozed until ${new Date(B.snoozedUntil).toLocaleString()} — it returns to the triage queue then.` : B.status === "SUBMITTED" ? "Waiting for a decision." : "Under review: accept it as planned, decline it, or merge it into its original."
133
+ }), /* @__PURE__ */ T(a, {
134
+ feature: B,
135
+ now: F
135
136
  })]
136
137
  })]
137
138
  }),
138
- z.status === "DECLINED" && z.declineReason && /* @__PURE__ */ w(D, {
139
+ B.status === "DECLINED" && B.declineReason && /* @__PURE__ */ T(O, {
139
140
  "data-testid": "decline-reason",
140
- children: /* @__PURE__ */ T(O, {
141
+ children: /* @__PURE__ */ E(k, {
141
142
  className: "pt-4 text-sm",
142
- children: [/* @__PURE__ */ w("span", {
143
+ children: [/* @__PURE__ */ T("span", {
143
144
  className: "font-medium",
144
145
  children: "Declined: "
145
- }), z.declineReason]
146
+ }), B.declineReason]
146
147
  })
147
148
  }),
148
- z.status === "DUPLICATE" && z.duplicateOfId && /* @__PURE__ */ w(D, {
149
+ B.status === "DUPLICATE" && B.duplicateOfId && /* @__PURE__ */ T(O, {
149
150
  "data-testid": "duplicate-of",
150
- children: /* @__PURE__ */ T(O, {
151
+ children: /* @__PURE__ */ E(k, {
151
152
  className: "pt-4 text-sm",
152
153
  children: [
153
- /* @__PURE__ */ w("span", {
154
+ /* @__PURE__ */ T("span", {
154
155
  className: "font-medium",
155
156
  children: "Duplicate of "
156
157
  }),
157
- z.duplicateOf ? /* @__PURE__ */ w("button", {
158
+ B.duplicateOf ? /* @__PURE__ */ T("button", {
158
159
  type: "button",
159
160
  className: "text-primary underline-offset-2 hover:underline",
160
- onClick: () => N(r("/products/feature-detail-page", z.duplicateOf.id)),
161
- children: z.duplicateOf.title
162
- }) : /* @__PURE__ */ w("span", {
161
+ onClick: () => P(r("/products/feature-detail-page", B.duplicateOf.id)),
162
+ children: B.duplicateOf.title
163
+ }) : /* @__PURE__ */ T("span", {
163
164
  className: "text-muted-foreground",
164
165
  children: "a request that has since been deleted"
165
166
  }),
@@ -168,60 +169,60 @@ function M() {
168
169
  ]
169
170
  })
170
171
  }),
171
- /* @__PURE__ */ T("div", {
172
+ /* @__PURE__ */ E("div", {
172
173
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
173
174
  children: [
174
- /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
175
+ /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
175
176
  className: "pt-4 text-center",
176
177
  children: [
177
- /* @__PURE__ */ w(v, { className: "h-5 w-5 mx-auto text-primary" }),
178
- /* @__PURE__ */ w("p", {
178
+ /* @__PURE__ */ T(y, { className: "h-5 w-5 mx-auto text-primary" }),
179
+ /* @__PURE__ */ T("p", {
179
180
  className: "text-2xl font-bold mt-1",
180
- children: z.priorityScore ?? 0
181
+ children: B.priorityScore ?? 0
181
182
  }),
182
- /* @__PURE__ */ w("p", {
183
+ /* @__PURE__ */ T("p", {
183
184
  className: "text-xs text-muted-foreground",
184
185
  children: "Priority Score"
185
186
  })
186
187
  ]
187
188
  }) }),
188
- /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
189
+ /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
189
190
  className: "pt-4 text-center",
190
191
  children: [
191
- /* @__PURE__ */ w(y, { className: "h-5 w-5 mx-auto text-green-500" }),
192
- /* @__PURE__ */ w("p", {
192
+ /* @__PURE__ */ T(b, { className: "h-5 w-5 mx-auto text-green-500" }),
193
+ /* @__PURE__ */ T("p", {
193
194
  className: "text-2xl font-bold mt-1",
194
- children: z.voteCount
195
+ children: B.voteCount
195
196
  }),
196
- /* @__PURE__ */ w("p", {
197
+ /* @__PURE__ */ T("p", {
197
198
  className: "text-xs text-muted-foreground",
198
199
  children: "Votes"
199
200
  })
200
201
  ]
201
202
  }) }),
202
- /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
203
+ /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
203
204
  className: "pt-4 text-center",
204
205
  children: [
205
- /* @__PURE__ */ w(_, { className: "h-5 w-5 mx-auto text-yellow-500" }),
206
- /* @__PURE__ */ w("p", {
206
+ /* @__PURE__ */ T(v, { className: "h-5 w-5 mx-auto text-yellow-500" }),
207
+ /* @__PURE__ */ T("p", {
207
208
  className: "text-2xl font-bold mt-1",
208
- children: z.businessValue ?? 0
209
+ children: B.businessValue ?? 0
209
210
  }),
210
- /* @__PURE__ */ w("p", {
211
+ /* @__PURE__ */ T("p", {
211
212
  className: "text-xs text-muted-foreground",
212
213
  children: "Business Value"
213
214
  })
214
215
  ]
215
216
  }) }),
216
- /* @__PURE__ */ w(D, { children: /* @__PURE__ */ T(O, {
217
+ /* @__PURE__ */ T(O, { children: /* @__PURE__ */ E(k, {
217
218
  className: "pt-4 text-center",
218
219
  children: [
219
- /* @__PURE__ */ w(g, { className: "h-5 w-5 mx-auto text-blue-500" }),
220
- /* @__PURE__ */ w("p", {
220
+ /* @__PURE__ */ T(_, { className: "h-5 w-5 mx-auto text-blue-500" }),
221
+ /* @__PURE__ */ T("p", {
221
222
  className: "text-2xl font-bold mt-1",
222
- children: V.length
223
+ children: H.length
223
224
  }),
224
- /* @__PURE__ */ w("p", {
225
+ /* @__PURE__ */ T("p", {
225
226
  className: "text-xs text-muted-foreground",
226
227
  children: "Competitors"
227
228
  })
@@ -229,147 +230,148 @@ function M() {
229
230
  }) })
230
231
  ]
231
232
  }),
232
- /* @__PURE__ */ T("div", {
233
+ /* @__PURE__ */ E("div", {
233
234
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
234
- children: [/* @__PURE__ */ T("div", {
235
+ children: [/* @__PURE__ */ E("div", {
235
236
  className: "lg:col-span-2 space-y-6",
236
237
  children: [
237
- /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Description" }) }), /* @__PURE__ */ w(O, { children: /* @__PURE__ */ w("p", {
238
+ /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Description" }) }), /* @__PURE__ */ T(k, { children: /* @__PURE__ */ T("p", {
238
239
  className: "text-sm",
239
- children: z.description ?? "No description provided."
240
+ children: B.description ?? "No description provided."
240
241
  }) })] }),
241
- /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ T(A, { children: [
242
+ /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ E(j, { children: [
242
243
  "Votes (",
243
- B.length,
244
+ V.length,
244
245
  ")"
245
- ] }) }), /* @__PURE__ */ T(O, {
246
+ ] }) }), /* @__PURE__ */ E(k, {
246
247
  className: "space-y-3",
247
- children: [B.length === 0 && /* @__PURE__ */ w("p", {
248
+ children: [V.length === 0 && /* @__PURE__ */ T("p", {
248
249
  className: "text-sm text-muted-foreground",
249
250
  children: "No votes yet."
250
- }), B.map((e) => /* @__PURE__ */ T("div", {
251
+ }), V.map((e) => /* @__PURE__ */ E("div", {
251
252
  className: "flex items-start justify-between border rounded-md p-3",
252
- children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ T("p", {
253
+ children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ E("p", {
253
254
  className: "text-sm font-medium",
254
- children: ["Voter: ", /* @__PURE__ */ w(n, { id: e.voterId })]
255
- }), e.comment && /* @__PURE__ */ w("p", {
255
+ children: ["Voter: ", /* @__PURE__ */ T(n, { id: e.voterId })]
256
+ }), e.comment && /* @__PURE__ */ T("p", {
256
257
  className: "text-xs text-muted-foreground mt-1",
257
258
  children: e.comment
258
- })] }), /* @__PURE__ */ T("div", {
259
+ })] }), /* @__PURE__ */ E("div", {
259
260
  className: "flex items-center gap-1",
260
- children: [/* @__PURE__ */ w(y, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ w("span", {
261
+ children: [/* @__PURE__ */ T(b, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ T("span", {
261
262
  className: "text-sm font-bold",
262
263
  children: e.weight
263
264
  })]
264
265
  })]
265
266
  }, e.id))]
266
267
  })] }),
267
- /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ T(A, { children: [
268
+ /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ E(j, { children: [
268
269
  "Competitors (",
269
- V.length,
270
+ H.length,
270
271
  ")"
271
- ] }) }), /* @__PURE__ */ w(O, { children: /* @__PURE__ */ w(i, {
272
- featureRequestId: z.id,
273
- competitors: V
272
+ ] }) }), /* @__PURE__ */ T(k, { children: /* @__PURE__ */ T(i, {
273
+ featureRequestId: B.id,
274
+ competitors: H
274
275
  }) })] })
275
276
  ]
276
- }), /* @__PURE__ */ T("div", {
277
+ }), /* @__PURE__ */ E("div", {
277
278
  className: "space-y-6",
278
279
  children: [
279
- /* @__PURE__ */ w(p, {
280
- featureId: z.id,
281
- productId: z.productId ?? null,
282
- initiative: z.roadmapItem ?? null
280
+ /* @__PURE__ */ T(m, { featureRequestId: B.id }),
281
+ /* @__PURE__ */ T(p, {
282
+ featureId: B.id,
283
+ productId: B.productId ?? null,
284
+ initiative: B.roadmapItem ?? null
283
285
  }),
284
- /* @__PURE__ */ w(d, { feature: z }),
285
- /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Details" }) }), /* @__PURE__ */ T(O, {
286
+ /* @__PURE__ */ T(d, { feature: B }),
287
+ /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Details" }) }), /* @__PURE__ */ E(k, {
286
288
  className: "space-y-3 text-sm",
287
289
  children: [
288
- /* @__PURE__ */ T("div", {
290
+ /* @__PURE__ */ E("div", {
289
291
  className: "flex justify-between",
290
- children: [/* @__PURE__ */ w("span", {
292
+ children: [/* @__PURE__ */ T("span", {
291
293
  className: "text-muted-foreground",
292
294
  children: "Status"
293
- }), /* @__PURE__ */ w(E, {
295
+ }), /* @__PURE__ */ T(D, {
294
296
  variant: "secondary",
295
297
  className: "capitalize",
296
- children: z.status.toLowerCase().replace("_", " ")
298
+ children: B.status.toLowerCase().replace("_", " ")
297
299
  })]
298
300
  }),
299
- /* @__PURE__ */ T("div", {
301
+ /* @__PURE__ */ E("div", {
300
302
  className: "flex justify-between",
301
- children: [/* @__PURE__ */ w("span", {
303
+ children: [/* @__PURE__ */ T("span", {
302
304
  className: "text-muted-foreground",
303
305
  children: "Priority Score"
304
- }), /* @__PURE__ */ w(E, {
306
+ }), /* @__PURE__ */ T(D, {
305
307
  variant: "outline",
306
- children: z.priorityScore ?? "N/A"
308
+ children: B.priorityScore ?? "N/A"
307
309
  })]
308
310
  }),
309
- /* @__PURE__ */ T("div", {
311
+ /* @__PURE__ */ E("div", {
310
312
  className: "flex justify-between",
311
- children: [/* @__PURE__ */ w("span", {
313
+ children: [/* @__PURE__ */ T("span", {
312
314
  className: "text-muted-foreground",
313
315
  children: "Effort"
314
- }), /* @__PURE__ */ w(E, {
316
+ }), /* @__PURE__ */ T(D, {
315
317
  variant: "outline",
316
318
  className: "capitalize",
317
- children: z.effortEstimate ?? "N/A"
319
+ children: B.effortEstimate ?? "N/A"
318
320
  })]
319
321
  }),
320
- /* @__PURE__ */ T("div", {
322
+ /* @__PURE__ */ E("div", {
321
323
  className: "flex justify-between",
322
- children: [/* @__PURE__ */ w("span", {
324
+ children: [/* @__PURE__ */ T("span", {
323
325
  className: "text-muted-foreground",
324
326
  children: "Business Value"
325
- }), /* @__PURE__ */ w("span", {
327
+ }), /* @__PURE__ */ T("span", {
326
328
  className: "font-medium",
327
- children: z.businessValue ?? "N/A"
329
+ children: B.businessValue ?? "N/A"
328
330
  })]
329
331
  }),
330
- /* @__PURE__ */ T("div", {
332
+ /* @__PURE__ */ E("div", {
331
333
  className: "flex justify-between items-center",
332
- children: [/* @__PURE__ */ w("span", {
334
+ children: [/* @__PURE__ */ T("span", {
333
335
  className: "text-muted-foreground",
334
336
  children: "Rating"
335
- }), /* @__PURE__ */ w(l, { rating: z.rating ?? null })]
337
+ }), /* @__PURE__ */ T(l, { rating: B.rating ?? null })]
336
338
  }),
337
- /* @__PURE__ */ T("div", {
339
+ /* @__PURE__ */ E("div", {
338
340
  className: "flex justify-between items-center",
339
- children: [/* @__PURE__ */ w("span", {
341
+ children: [/* @__PURE__ */ T("span", {
340
342
  className: "text-muted-foreground",
341
343
  children: "Category"
342
- }), z.category ? /* @__PURE__ */ w(c, { category: z.category }) : /* @__PURE__ */ w("span", {
344
+ }), B.category ? /* @__PURE__ */ T(c, { category: B.category }) : /* @__PURE__ */ T("span", {
343
345
  className: "text-sm text-muted-foreground",
344
346
  children: "Not set"
345
347
  })]
346
348
  }),
347
- /* @__PURE__ */ T("div", {
349
+ /* @__PURE__ */ E("div", {
348
350
  className: "flex justify-between",
349
- children: [/* @__PURE__ */ w("span", {
351
+ children: [/* @__PURE__ */ T("span", {
350
352
  className: "text-muted-foreground",
351
353
  children: "Submitted By"
352
- }), /* @__PURE__ */ w(n, { id: z.submittedBy })]
354
+ }), /* @__PURE__ */ T(n, { id: B.submittedBy })]
353
355
  }),
354
- z.triagedAt && /* @__PURE__ */ T("div", {
356
+ B.triagedAt && /* @__PURE__ */ E("div", {
355
357
  className: "flex justify-between",
356
- children: [/* @__PURE__ */ w("span", {
358
+ children: [/* @__PURE__ */ T("span", {
357
359
  className: "text-muted-foreground",
358
360
  children: "Decided"
359
- }), /* @__PURE__ */ T("span", { children: [new Date(z.triagedAt).toLocaleDateString(), z.triagedBy && /* @__PURE__ */ T(C, { children: [" by ", /* @__PURE__ */ w(n, { id: z.triagedBy })] })] })]
361
+ }), /* @__PURE__ */ E("span", { children: [new Date(B.triagedAt).toLocaleDateString(), B.triagedBy && /* @__PURE__ */ E(w, { children: [" by ", /* @__PURE__ */ T(n, { id: B.triagedBy })] })] })]
360
362
  }),
361
- /* @__PURE__ */ T("div", {
363
+ /* @__PURE__ */ E("div", {
362
364
  className: "flex justify-between",
363
- children: [/* @__PURE__ */ w("span", {
365
+ children: [/* @__PURE__ */ T("span", {
364
366
  className: "text-muted-foreground",
365
367
  children: "Updated"
366
- }), /* @__PURE__ */ w("span", { children: new Date(z.updatedAt).toLocaleDateString() })]
368
+ }), /* @__PURE__ */ T("span", { children: new Date(B.updatedAt).toLocaleDateString() })]
367
369
  })
368
370
  ]
369
371
  })] }),
370
- z.tags?.length > 0 && /* @__PURE__ */ T(D, { children: [/* @__PURE__ */ w(k, { children: /* @__PURE__ */ w(A, { children: "Tags" }) }), /* @__PURE__ */ w(O, {
372
+ B.tags?.length > 0 && /* @__PURE__ */ E(O, { children: [/* @__PURE__ */ T(A, { children: /* @__PURE__ */ T(j, { children: "Tags" }) }), /* @__PURE__ */ T(k, {
371
373
  className: "flex flex-wrap gap-1",
372
- children: z.tags.map((e) => /* @__PURE__ */ w(E, {
374
+ children: B.tags.map((e) => /* @__PURE__ */ T(D, {
373
375
  variant: "outline",
374
376
  className: "text-xs",
375
377
  children: e
@@ -382,6 +384,6 @@ function M() {
382
384
  });
383
385
  }
384
386
  //#endregion
385
- export { M as ProductsFeatureDetailPagePage };
387
+ export { N as ProductsFeatureDetailPagePage };
386
388
 
387
389
  //# sourceMappingURL=ProductsFeatureDetailPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.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 } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\nimport { CategoryBadge, RatingDisplay } from '../controls/FeedbackControls';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n rating\n category\n isAnonymous\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n reach\n impact\n confidence\n riceScore\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return <div className=\"p-6 text-destructive\">Failed to load feature: {error.message}</div>;\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\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\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> &middot; Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n key={feature.id}\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\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\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n {/* CWF-15 */}\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Rating</span>\n <RatingDisplay rating={feature.rating ?? null} />\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Category</span>\n {feature.category ? (\n <CategoryBadge category={feature.category} />\n ) : (\n <span className=\"text-sm text-muted-foreground\">Not set</span>\n )}\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsFeatureDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeatureDetailPagePage.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 } from '@burdenoff/fe-libs/ui';\nimport { Star, ThumbsUp, MessageSquare, Target, Loader2 } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { FeatureTriageActions, isOpenRequest, isSnoozed } from '../controls/FeatureTriage';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport {\n FeatureHeaderActions,\n FeaturePrioritizationCard,\n FeatureVoteButton,\n} from '../controls/FeatureControls';\nimport { CompetitorLinkCard } from '../controls/CompetitorControls';\nimport { FeatureInitiativeCard } from '../roadmaps/FeatureInitiativeCard';\nimport { CategoryBadge, RatingDisplay } from '../controls/FeedbackControls';\nimport { ProductFeatureLinkCard } from '../product/ProductFeatureLinkCard';\n\nconst GET_FEATURE_REQUEST = gql`\n query GetFeatureRequest($id: ID!) {\n planmagnetFeatureRequest(id: $id) {\n id\n workspaceId\n projectId\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n workItemId\n tags\n snoozedUntil\n rating\n category\n isAnonymous\n moscow\n kano\n valueScore\n effortScore\n valueEffortQuadrant\n reach\n impact\n confidence\n riceScore\n declineReason\n duplicateOfId\n duplicateOf {\n id\n title\n }\n triagedAt\n triagedBy\n createdAt\n updatedAt\n productId\n roadmapItem {\n id\n title\n roadmap {\n id\n name\n }\n }\n votes {\n id\n voterId\n weight\n comment\n createdAt\n }\n competitors {\n id\n name\n description\n website\n }\n }\n }\n`;\n\ninterface FeatureVoteNode {\n id: string;\n voterId: string;\n weight: number;\n comment: string | null;\n createdAt: string;\n}\n\ninterface CompetitorNode {\n id: string;\n name: string;\n description: string | null;\n website: string | null;\n}\n\nexport function ProductsFeatureDetailPagePage() {\n const { workspaceId, currentUser } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n const params = useParams<{ id: string }>();\n const featureId = params.id ?? '';\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_FEATURE_REQUEST, {\n variables: { id: featureId },\n skip: !featureId,\n });\n\n // Only before the first load: a refetch must not blank the page (it\n\n // unmounts every component on it and loses their state).\n\n if (loading && !data) {\n return (\n <div className=\"flex items-center justify-center p-12\">\n <Loader2 className=\"h-8 w-8 animate-spin text-muted-foreground\" />\n </div>\n );\n }\n\n if (error) {\n return (\n <div className=\"p-6 text-destructive\">Failed to load feature request: {error.message}</div>\n );\n }\n\n const feature = data?.planmagnetFeatureRequest;\n if (!feature) return <div className=\"p-6 text-muted-foreground\">Feature request not found.</div>;\n\n const votes: FeatureVoteNode[] = feature.votes ?? [];\n const competitors: CompetitorNode[] = feature.competitors ?? [];\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\">{feature.title}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Submitted by: <PersonName id={feature.submittedBy} /> &middot; Effort:{' '}\n {feature.effortEstimate ?? 'N/A'}\n </p>\n </div>\n {/* Vote, status, Edit and Delete (BOFF-7951): the request was read-only. */}\n <div className=\"flex flex-col items-end gap-2\">\n <FeatureVoteButton\n key={feature.id}\n featureId={feature.id}\n voted={votes.some((v) => v.voterId === currentUser?.id)}\n />\n <FeatureHeaderActions feature={feature} />\n </div>\n </div>\n\n {/* Triage (P1.3): an undecided request is accepted, declined, snoozed or merged from here. */}\n {isOpenRequest(feature.status) && (\n <Card data-testid=\"triage-card\">\n <CardHeader>\n <CardTitle>Triage</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">\n {isSnoozed(feature.snoozedUntil, now)\n ? `Snoozed until ${new Date(feature.snoozedUntil).toLocaleString()} — it returns to the triage queue then.`\n : feature.status === 'SUBMITTED'\n ? 'Waiting for a decision.'\n : 'Under review: accept it as planned, decline it, or merge it into its original.'}\n </p>\n <FeatureTriageActions feature={feature} now={now} />\n </CardContent>\n </Card>\n )}\n {feature.status === 'DECLINED' && feature.declineReason && (\n <Card data-testid=\"decline-reason\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Declined: </span>\n {feature.declineReason}\n </CardContent>\n </Card>\n )}\n {feature.status === 'DUPLICATE' && feature.duplicateOfId && (\n <Card data-testid=\"duplicate-of\">\n <CardContent className=\"pt-4 text-sm\">\n <span className=\"font-medium\">Duplicate of </span>\n {feature.duplicateOf ? (\n <button\n type=\"button\"\n className=\"text-primary underline-offset-2 hover:underline\"\n onClick={() =>\n navigate(detailPath('/products/feature-detail-page', feature.duplicateOf.id))\n }\n >\n {feature.duplicateOf.title}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">a request that has since been deleted</span>\n )}{' '}\n — its votes were moved there.\n </CardContent>\n </Card>\n )}\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Target className=\"h-5 w-5 mx-auto text-primary\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.priorityScore ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Priority Score</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <ThumbsUp className=\"h-5 w-5 mx-auto text-green-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.voteCount}</p>\n <p className=\"text-xs text-muted-foreground\">Votes</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <Star className=\"h-5 w-5 mx-auto text-yellow-500\" />\n <p className=\"text-2xl font-bold mt-1\">{feature.businessValue ?? 0}</p>\n <p className=\"text-xs text-muted-foreground\">Business Value</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4 text-center\">\n <MessageSquare className=\"h-5 w-5 mx-auto text-blue-500\" />\n <p className=\"text-2xl font-bold mt-1\">{competitors.length}</p>\n <p className=\"text-xs text-muted-foreground\">Competitors</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Description</CardTitle>\n </CardHeader>\n <CardContent>\n <p className=\"text-sm\">{feature.description ?? 'No description provided.'}</p>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Votes ({votes.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {votes.length === 0 && <p className=\"text-sm text-muted-foreground\">No votes yet.</p>}\n {votes.map((vote) => (\n <div\n key={vote.id}\n className=\"flex items-start justify-between border rounded-md p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">\n Voter: <PersonName id={vote.voterId} />\n </p>\n {vote.comment && (\n <p className=\"text-xs text-muted-foreground mt-1\">{vote.comment}</p>\n )}\n </div>\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\">{vote.weight}</span>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n {/* Competitor tracking (BOFF-8047): add, remove and browse to the competitors linked here. */}\n <Card>\n <CardHeader>\n <CardTitle>Competitors ({competitors.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <CompetitorLinkCard featureRequestId={feature.id} competitors={competitors} />\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n {/* PRM-01: the product feature (a capability the team builds) that answers this request. */}\n <ProductFeatureLinkCard featureRequestId={feature.id} />\n {/* PRM-24: the roadmap initiative that addresses this request. */}\n <FeatureInitiativeCard\n featureId={feature.id}\n productId={feature.productId ?? null}\n initiative={feature.roadmapItem ?? null}\n />\n {/* PRM-27: MoSCoW/Kano/Value-Effort scoring, edited in place with the computed quadrant read back\n from the save's own response. */}\n <FeaturePrioritizationCard feature={feature} />\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant=\"secondary\" className=\"capitalize\">\n {feature.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Priority Score</span>\n <Badge variant=\"outline\">{feature.priorityScore ?? 'N/A'}</Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Effort</span>\n <Badge variant=\"outline\" className=\"capitalize\">\n {feature.effortEstimate ?? 'N/A'}\n </Badge>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Business Value</span>\n <span className=\"font-medium\">{feature.businessValue ?? 'N/A'}</span>\n </div>\n {/* CWF-15 */}\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Rating</span>\n <RatingDisplay rating={feature.rating ?? null} />\n </div>\n <div className=\"flex justify-between items-center\">\n <span className=\"text-muted-foreground\">Category</span>\n {feature.category ? (\n <CategoryBadge category={feature.category} />\n ) : (\n <span className=\"text-sm text-muted-foreground\">Not set</span>\n )}\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Submitted By</span>\n <PersonName id={feature.submittedBy} />\n </div>\n {feature.triagedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Decided</span>\n <span>\n {new Date(feature.triagedAt).toLocaleDateString()}\n {feature.triagedBy && (\n <>\n {' by '}\n <PersonName id={feature.triagedBy} />\n </>\n )}\n </span>\n </div>\n )}\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n </CardContent>\n </Card>\n\n {feature.tags?.length > 0 && (\n <Card>\n <CardHeader>\n <CardTitle>Tags</CardTitle>\n </CardHeader>\n <CardContent className=\"flex flex-wrap gap-1\">\n {feature.tags.map((tag: string) => (\n <Badge key={tag} variant=\"outline\" className=\"text-xs\">\n {tag}\n </Badge>\n ))}\n </CardContent>\n </Card>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AA8GA,wBAAgB,wBAAwB,4CAkXvC"}
1
+ {"version":3,"file":"ProductsFeaturesPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"names":[],"mappings":"AA8GA,wBAAgB,wBAAwB,4CAsXvC"}
@@ -84,7 +84,7 @@ function M() {
84
84
  });
85
85
  if (Y) return /* @__PURE__ */ T("div", {
86
86
  className: "p-6 text-destructive",
87
- children: ["Failed to load features: ", Y.message]
87
+ children: ["Failed to load feature requests: ", Y.message]
88
88
  });
89
89
  let X = J?.planmagnetFeatureRequests?.items ?? [], Z = J?.planmagnetFeatureRequests?.totalCount ?? 0, Q = R ? [...X].sort((e, t) => e.riceScore == null && t.riceScore == null ? 0 : e.riceScore == null ? 1 : t.riceScore == null ? -1 : t.riceScore - e.riceScore) : X, se = [
90
90
  "all",
@@ -110,14 +110,14 @@ function M() {
110
110
  className: "flex items-center justify-between",
111
111
  children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("h1", {
112
112
  className: "text-2xl font-semibold",
113
- children: "Features"
113
+ children: "Feature requests"
114
114
  }), /* @__PURE__ */ w("p", {
115
115
  className: "text-sm text-muted-foreground",
116
116
  children: N === "all" ? `${$.total} feature requests` : N === "TRIAGE" ? `${Z} waiting for triage` : `${Z} ${N.toLowerCase().replace("_", " ")} match`
117
117
  })] }), /* @__PURE__ */ T(D, {
118
118
  size: "sm",
119
119
  onClick: () => S("/products/new-feature-page"),
120
- children: [/* @__PURE__ */ w(re, { className: "mr-1 h-4 w-4" }), "New Feature"]
120
+ children: [/* @__PURE__ */ w(re, { className: "mr-1 h-4 w-4" }), "New feature request"]
121
121
  })]
122
122
  }),
123
123
  /* @__PURE__ */ T("div", {
@@ -127,7 +127,7 @@ function M() {
127
127
  className: "pt-4",
128
128
  children: [/* @__PURE__ */ w("p", {
129
129
  className: "text-xs text-muted-foreground",
130
- children: "Total Features"
130
+ children: "Total feature requests"
131
131
  }), /* @__PURE__ */ w("p", {
132
132
  className: "text-xl font-bold",
133
133
  children: $.total
@@ -397,7 +397,7 @@ function M() {
397
397
  }),
398
398
  Q.length === 0 && /* @__PURE__ */ w("p", {
399
399
  className: "text-center text-muted-foreground py-8",
400
- children: N === "TRIAGE" ? "Nothing waits for triage." : "No features match the selected filters."
400
+ children: N === "TRIAGE" ? "Nothing waits for triage." : "No feature requests match the selected filters."
401
401
  })
402
402
  ]
403
403
  });