@burdenoff/microfe-planmagnet 2026.927.4 → 2026.927.5

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 (137) hide show
  1. package/dist/generated/global-types.d.ts +9 -1
  2. package/dist/generated/global-types.d.ts.map +1 -1
  3. package/dist/generated/wspace-operations.d.ts +0 -12
  4. package/dist/generated/wspace-operations.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
  7. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  8. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  9. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
  10. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  11. package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
  12. package/dist/planmagnet/controls/FeatureControls.js +142 -139
  13. package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
  14. package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
  15. package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
  16. package/dist/planmagnet/controls/FeatureTriage.js +398 -0
  17. package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
  18. package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -1
  19. package/dist/planmagnet/controls/MilestoneControls.js +76 -73
  20. package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
  21. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  22. package/dist/planmagnet/controls/ProjectControls.js +47 -44
  23. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  24. package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -1
  25. package/dist/planmagnet/controls/ReleaseControls.js +47 -44
  26. package/dist/planmagnet/controls/ReleaseControls.js.map +1 -1
  27. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  28. package/dist/planmagnet/controls/WorkItemControls.js +68 -65
  29. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  30. package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
  31. package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
  32. package/dist/planmagnet/hooks/useCounts.js +209 -0
  33. package/dist/planmagnet/hooks/useCounts.js.map +1 -0
  34. package/dist/planmagnet/import/exportColumns.d.ts +52 -0
  35. package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
  36. package/dist/planmagnet/import/exportColumns.js +81 -0
  37. package/dist/planmagnet/import/exportColumns.js.map +1 -0
  38. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  39. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
  40. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  41. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  42. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
  43. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  44. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  45. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +147 -144
  46. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  47. package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
  48. package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
  49. package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
  50. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  51. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +173 -173
  52. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  53. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  54. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +120 -117
  55. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  56. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
  57. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
  58. package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
  59. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
  60. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +168 -103
  61. package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
  62. package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
  63. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
  64. package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
  65. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  66. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
  67. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  68. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  69. package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
  70. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  71. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  72. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +12 -2
  73. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  74. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  75. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -2
  76. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  77. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  78. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
  79. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  80. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  81. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
  82. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  83. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
  84. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
  85. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
  86. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
  87. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  88. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
  89. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  90. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
  91. package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
  92. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
  93. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
  94. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  95. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
  96. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  97. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  98. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
  99. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  100. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
  101. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  102. package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
  103. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  104. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  105. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
  106. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  107. package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
  108. package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
  109. package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
  110. package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
  111. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  112. package/dist/planmagnet/pages/TasksTasksPagePage.js +110 -104
  113. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  114. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  115. package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
  116. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  117. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  118. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
  119. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  120. package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
  121. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
  122. package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
  123. package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
  124. package/dist/planmagnet/roadmaps/RoadmapControls.js +151 -138
  125. package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
  126. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  127. package/dist/planmagnet/sprints/SprintControls.js +32 -30
  128. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  129. package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
  130. package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
  131. package/dist/planmagnet/utils/mutationResult.js +2 -1
  132. package/dist/planmagnet/utils/mutationResult.js.map +1 -1
  133. package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
  134. package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
  135. package/dist/planmagnet/utils/rowNavigation.js +9 -8
  136. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  137. package/package.json +1 -1
@@ -1,20 +1,28 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as n } from "../people/People.js";
3
- import { detailPath as r, rowLinkProps as i } from "../utils/rowNavigation.js";
4
- import { useState as a } from "react";
5
- import { ArrowUpDown as o, Filter as s, Loader2 as c, Plus as l, Star as u, Target as d, ThumbsUp as f } from "lucide-react";
6
- import { gql as p } from "@apollo/client";
7
- import { useQuery as m } from "@apollo/client/react";
8
- import { jsx as h, jsxs as g } from "react/jsx-runtime";
9
- import { Badge as _, Button as v, Card as y, CardContent as b } from "@burdenoff/fe-libs/ui";
3
+ import { countLabel as r, useFeatureCounts as i } from "../hooks/useCounts.js";
4
+ import { detailPath as a, rowLinkProps as o } from "../utils/rowNavigation.js";
5
+ import { FeatureTriageActions as s, isSnoozed as c } from "../controls/FeatureTriage.js";
6
+ import { useState as l } from "react";
7
+ import { ArrowUpDown as u, Filter as d, Loader2 as f, Plus as p, Star as m, Target as h, ThumbsUp as g } from "lucide-react";
8
+ import { gql as _ } from "@apollo/client";
9
+ import { useQuery as v } from "@apollo/client/react";
10
+ import { jsx as y, jsxs as b } from "react/jsx-runtime";
11
+ import { Badge as x, Button as S, Card as C, CardContent as w } from "@burdenoff/fe-libs/ui";
10
12
  //#region src/planmagnet/pages/ProductsFeaturesPagePage.tsx
11
- var x = p`
13
+ var T = _`
12
14
  query ListFeatureRequests(
13
15
  $workspaceId: ID!
14
16
  $filter: PlanmagnetFeatureRequestFilter
15
17
  $pagination: PlanmagnetPaginationInput
18
+ $orderBy: PlanmagnetFeatureOrder
16
19
  ) {
17
- planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {
20
+ planmagnetFeatureRequests(
21
+ workspaceId: $workspaceId
22
+ filter: $filter
23
+ pagination: $pagination
24
+ orderBy: $orderBy
25
+ ) {
18
26
  items {
19
27
  id
20
28
  title
@@ -26,13 +34,15 @@ var x = p`
26
34
  voteCount
27
35
  submittedBy
28
36
  tags
37
+ projectId
38
+ snoozedUntil
29
39
  createdAt
30
40
  updatedAt
31
41
  }
32
42
  totalCount
33
43
  }
34
44
  }
35
- `, S = {
45
+ `, E = {
36
46
  SUBMITTED: "bg-gray-100 text-gray-800",
37
47
  UNDER_REVIEW: "bg-yellow-100 text-yellow-800",
38
48
  PLANNED: "bg-blue-100 text-blue-800",
@@ -41,28 +51,28 @@ var x = p`
41
51
  DECLINED: "bg-red-100 text-red-800",
42
52
  DUPLICATE: "bg-orange-100 text-orange-800"
43
53
  };
44
- function C() {
45
- let p = t(), { workspaceId: C } = e(), [w, T] = a("all"), [E, D] = a("priorityScore"), { data: O, loading: k, error: A } = m(x, {
54
+ function D() {
55
+ let _ = t(), { workspaceId: D } = e(), [O, k] = l("all"), [A] = l(() => Date.now()), [j, M] = l("NEWEST"), N = i(), { data: P, loading: F, error: I } = v(T, {
46
56
  variables: {
47
- workspaceId: C,
48
- filter: w === "all" ? void 0 : { status: w },
49
- pagination: { first: 50 }
57
+ workspaceId: D,
58
+ filter: O === "TRIAGE" ? { triage: !0 } : O === "SNOOZED" ? { snoozed: !0 } : O === "all" ? void 0 : { status: O },
59
+ pagination: { first: 50 },
60
+ orderBy: j
50
61
  },
51
- skip: !C
62
+ skip: !D
52
63
  });
53
- if (k && !O) return /* @__PURE__ */ h("div", {
64
+ if (F && !P) return /* @__PURE__ */ y("div", {
54
65
  className: "flex items-center justify-center p-12",
55
- children: /* @__PURE__ */ h(c, { className: "h-8 w-8 animate-spin text-muted-foreground" })
66
+ children: /* @__PURE__ */ y(f, { className: "h-8 w-8 animate-spin text-muted-foreground" })
56
67
  });
57
- if (A) return /* @__PURE__ */ g("div", {
68
+ if (I) return /* @__PURE__ */ b("div", {
58
69
  className: "p-6 text-destructive",
59
- children: ["Failed to load features: ", A.message]
70
+ children: ["Failed to load features: ", I.message]
60
71
  });
61
- let j = O?.planmagnetFeatureRequests?.items ?? [], M = O?.planmagnetFeatureRequests?.totalCount ?? 0, N = [...j].sort((e, t) => {
62
- let n = e[E] ?? 0;
63
- return (t[E] ?? 0) - n;
64
- }), P = [
72
+ let L = P?.planmagnetFeatureRequests?.items ?? [], R = P?.planmagnetFeatureRequests?.totalCount ?? 0, z = L, B = [
65
73
  "all",
74
+ "TRIAGE",
75
+ "SNOOZED",
66
76
  "SUBMITTED",
67
77
  "UNDER_REVIEW",
68
78
  "PLANNED",
@@ -70,176 +80,180 @@ function C() {
70
80
  "COMPLETED",
71
81
  "DECLINED",
72
82
  "DUPLICATE"
73
- ], F = {
74
- total: M,
75
- inProgress: j.filter((e) => e.status === "IN_PROGRESS").length,
76
- completed: j.filter((e) => e.status === "COMPLETED").length,
77
- avgPriority: j.length > 0 ? Math.round(j.reduce((e, t) => e + (t.priorityScore ?? 0), 0) / j.length) : 0
83
+ ], V = {
84
+ total: r(N?.all),
85
+ open: r(N?.open),
86
+ inProgress: r(N?.byStatus.IN_PROGRESS),
87
+ completed: r(N?.byStatus.COMPLETED)
78
88
  };
79
- return /* @__PURE__ */ g("div", {
89
+ return /* @__PURE__ */ b("div", {
80
90
  className: "space-y-6 p-6",
81
91
  children: [
82
- /* @__PURE__ */ g("div", {
92
+ /* @__PURE__ */ b("div", {
83
93
  className: "flex items-center justify-between",
84
- children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
94
+ children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
85
95
  className: "text-2xl font-semibold",
86
96
  children: "Features"
87
- }), /* @__PURE__ */ g("p", {
97
+ }), /* @__PURE__ */ y("p", {
88
98
  className: "text-sm text-muted-foreground",
89
- children: [M, " feature requests across all products"]
90
- })] }), /* @__PURE__ */ g(v, {
99
+ children: O === "all" ? `${V.total} feature requests` : O === "TRIAGE" ? `${R} waiting for triage` : `${R} ${O.toLowerCase().replace("_", " ")} match`
100
+ })] }), /* @__PURE__ */ b(S, {
91
101
  size: "sm",
92
- onClick: () => p("/products/new-feature-page"),
93
- children: [/* @__PURE__ */ h(l, { className: "mr-1 h-4 w-4" }), "New Feature"]
102
+ onClick: () => _("/products/new-feature-page"),
103
+ children: [/* @__PURE__ */ y(p, { className: "mr-1 h-4 w-4" }), "New Feature"]
94
104
  })]
95
105
  }),
96
- /* @__PURE__ */ g("div", {
106
+ /* @__PURE__ */ b("div", {
97
107
  className: "grid grid-cols-2 md:grid-cols-4 gap-4",
98
108
  children: [
99
- /* @__PURE__ */ h(y, { children: /* @__PURE__ */ g(b, {
109
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
100
110
  className: "pt-4",
101
- children: [/* @__PURE__ */ h("p", {
111
+ children: [/* @__PURE__ */ y("p", {
102
112
  className: "text-xs text-muted-foreground",
103
113
  children: "Total Features"
104
- }), /* @__PURE__ */ h("p", {
114
+ }), /* @__PURE__ */ y("p", {
105
115
  className: "text-xl font-bold",
106
- children: F.total
116
+ children: V.total
107
117
  })]
108
118
  }) }),
109
- /* @__PURE__ */ h(y, { children: /* @__PURE__ */ g(b, {
119
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
110
120
  className: "pt-4",
111
- children: [/* @__PURE__ */ h("p", {
121
+ children: [/* @__PURE__ */ y("p", {
112
122
  className: "text-xs text-muted-foreground",
113
123
  children: "In Progress"
114
- }), /* @__PURE__ */ h("p", {
124
+ }), /* @__PURE__ */ y("p", {
115
125
  className: "text-xl font-bold",
116
- children: F.inProgress
126
+ children: V.inProgress
117
127
  })]
118
128
  }) }),
119
- /* @__PURE__ */ h(y, { children: /* @__PURE__ */ g(b, {
129
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
120
130
  className: "pt-4",
121
- children: [/* @__PURE__ */ h("p", {
131
+ children: [/* @__PURE__ */ y("p", {
122
132
  className: "text-xs text-muted-foreground",
123
133
  children: "Completed"
124
- }), /* @__PURE__ */ h("p", {
134
+ }), /* @__PURE__ */ y("p", {
125
135
  className: "text-xl font-bold",
126
- children: F.completed
136
+ children: V.completed
127
137
  })]
128
138
  }) }),
129
- /* @__PURE__ */ h(y, { children: /* @__PURE__ */ g(b, {
139
+ /* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
130
140
  className: "pt-4",
131
- children: [/* @__PURE__ */ h("p", {
141
+ children: [/* @__PURE__ */ y("p", {
132
142
  className: "text-xs text-muted-foreground",
133
- children: "Avg Priority Score"
134
- }), /* @__PURE__ */ h("p", {
143
+ children: "Open"
144
+ }), /* @__PURE__ */ y("p", {
135
145
  className: "text-xl font-bold",
136
- children: F.avgPriority
146
+ children: V.open
137
147
  })]
138
148
  }) })
139
149
  ]
140
150
  }),
141
- /* @__PURE__ */ g("div", {
151
+ /* @__PURE__ */ b("div", {
142
152
  className: "flex items-center justify-between flex-wrap gap-2",
143
- children: [/* @__PURE__ */ g("div", {
153
+ children: [/* @__PURE__ */ b("div", {
144
154
  className: "flex items-center gap-2",
145
- children: [/* @__PURE__ */ h(s, { className: "h-4 w-4 text-muted-foreground" }), P.map((e) => /* @__PURE__ */ h("button", {
146
- onClick: () => T(e),
147
- className: `px-2 py-1 text-xs rounded-md capitalize ${w === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
148
- children: e === "all" ? "All" : e.toLowerCase().replace("_", " ")
155
+ children: [/* @__PURE__ */ y(d, { className: "h-4 w-4 text-muted-foreground" }), B.map((e) => /* @__PURE__ */ y("button", {
156
+ onClick: () => k(e),
157
+ className: `px-2 py-1 text-xs rounded-md capitalize ${O === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
158
+ children: e === "all" ? "All" : e === "TRIAGE" ? `Triage (${r(N?.triage)})` : e === "SNOOZED" ? `Snoozed (${r(N?.snoozed)})` : e.toLowerCase().replace("_", " ")
149
159
  }, e))]
150
- }), /* @__PURE__ */ g("div", {
160
+ }), /* @__PURE__ */ b("div", {
151
161
  className: "flex items-center gap-2",
152
- children: [/* @__PURE__ */ h(o, { className: "h-4 w-4 text-muted-foreground" }), [
153
- "priorityScore",
154
- "voteCount",
155
- "businessValue"
156
- ].map((e) => /* @__PURE__ */ h("button", {
157
- onClick: () => D(e),
158
- className: `px-2 py-1 text-xs rounded-md capitalize ${E === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
159
- children: e === "priorityScore" ? "Priority" : e === "voteCount" ? "Votes" : "Value"
162
+ children: [/* @__PURE__ */ y(u, { className: "h-4 w-4 text-muted-foreground" }), ["NEWEST", "MOST_VOTED"].map((e) => /* @__PURE__ */ y("button", {
163
+ onClick: () => M(e),
164
+ className: `px-2 py-1 text-xs rounded-md ${j === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
165
+ children: e === "NEWEST" ? "Newest" : "Most voted"
160
166
  }, e))]
161
167
  })]
162
168
  }),
163
- /* @__PURE__ */ h("div", {
169
+ /* @__PURE__ */ y("div", {
164
170
  className: "space-y-3",
165
- children: N.map((e) => /* @__PURE__ */ h(y, {
166
- ...i(() => p(r("/products/feature-detail-page", e.id)), e.title),
167
- children: /* @__PURE__ */ h(b, {
171
+ children: z.map((e) => /* @__PURE__ */ y(C, {
172
+ ...o(() => _(a("/products/feature-detail-page", e.id)), e.title),
173
+ children: /* @__PURE__ */ y(w, {
168
174
  className: "py-4",
169
- children: /* @__PURE__ */ g("div", {
175
+ children: /* @__PURE__ */ b("div", {
170
176
  className: "flex items-start justify-between gap-4",
171
- children: [/* @__PURE__ */ g("div", {
177
+ children: [/* @__PURE__ */ b("div", {
172
178
  className: "flex-1 space-y-2",
173
179
  children: [
174
- /* @__PURE__ */ g("div", {
180
+ /* @__PURE__ */ b("div", {
175
181
  className: "flex items-center gap-2",
176
- children: [/* @__PURE__ */ h("span", {
182
+ children: [/* @__PURE__ */ y("span", {
177
183
  className: "text-sm font-medium",
178
184
  children: e.title
179
- }), /* @__PURE__ */ h("span", {
180
- className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${S[e.status] ?? "bg-gray-100"}`,
185
+ }), /* @__PURE__ */ y("span", {
186
+ className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${E[e.status] ?? "bg-gray-100"}`,
181
187
  children: e.status.toLowerCase().replace("_", " ")
182
188
  })]
183
189
  }),
184
- /* @__PURE__ */ h("p", {
190
+ /* @__PURE__ */ y("p", {
185
191
  className: "text-xs text-muted-foreground",
186
192
  children: e.description
187
193
  }),
188
- /* @__PURE__ */ g("div", {
194
+ /* @__PURE__ */ b("div", {
189
195
  className: "flex items-center gap-4 text-xs text-muted-foreground",
190
196
  children: [
191
- /* @__PURE__ */ g("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
192
- /* @__PURE__ */ g("span", { children: ["Submitted by: ", /* @__PURE__ */ h(n, { id: e.submittedBy })] }),
193
- /* @__PURE__ */ h("span", { children: new Date(e.updatedAt).toLocaleDateString() })
197
+ /* @__PURE__ */ b("span", { children: ["Effort: ", e.effortEstimate ?? "N/A"] }),
198
+ /* @__PURE__ */ b("span", { children: ["Submitted by: ", /* @__PURE__ */ y(n, { id: e.submittedBy })] }),
199
+ /* @__PURE__ */ y("span", { children: new Date(e.updatedAt).toLocaleDateString() })
194
200
  ]
195
201
  }),
196
- e.tags.length > 0 && /* @__PURE__ */ h("div", {
202
+ c(e.snoozedUntil, A) && /* @__PURE__ */ b("p", {
203
+ className: "text-xs text-muted-foreground",
204
+ children: ["Snoozed until ", new Date(e.snoozedUntil).toLocaleDateString()]
205
+ }),
206
+ /* @__PURE__ */ y(s, {
207
+ feature: e,
208
+ now: A
209
+ }),
210
+ e.tags.length > 0 && /* @__PURE__ */ y("div", {
197
211
  className: "flex gap-1",
198
- children: e.tags.map((e) => /* @__PURE__ */ h(_, {
212
+ children: e.tags.map((e) => /* @__PURE__ */ y(x, {
199
213
  variant: "outline",
200
214
  className: "text-[10px]",
201
215
  children: e
202
216
  }, e))
203
217
  })
204
218
  ]
205
- }), /* @__PURE__ */ g("div", {
219
+ }), /* @__PURE__ */ b("div", {
206
220
  className: "flex items-center gap-4 shrink-0",
207
221
  children: [
208
- /* @__PURE__ */ g("div", {
222
+ /* @__PURE__ */ b("div", {
209
223
  className: "text-center",
210
- children: [/* @__PURE__ */ g("div", {
224
+ children: [/* @__PURE__ */ b("div", {
211
225
  className: "flex items-center gap-1",
212
- children: [/* @__PURE__ */ h(d, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ h("span", {
226
+ children: [/* @__PURE__ */ y(h, { className: "h-3 w-3 text-primary" }), /* @__PURE__ */ y("span", {
213
227
  className: "text-sm font-bold",
214
228
  children: e.priorityScore ?? 0
215
229
  })]
216
- }), /* @__PURE__ */ h("p", {
230
+ }), /* @__PURE__ */ y("p", {
217
231
  className: "text-[10px] text-muted-foreground",
218
232
  children: "Priority"
219
233
  })]
220
234
  }),
221
- /* @__PURE__ */ g("div", {
235
+ /* @__PURE__ */ b("div", {
222
236
  className: "text-center",
223
- children: [/* @__PURE__ */ g("div", {
237
+ children: [/* @__PURE__ */ b("div", {
224
238
  className: "flex items-center gap-1",
225
- children: [/* @__PURE__ */ h(f, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ h("span", {
239
+ children: [/* @__PURE__ */ y(g, { className: "h-3 w-3 text-green-500" }), /* @__PURE__ */ y("span", {
226
240
  className: "text-sm font-bold",
227
241
  children: e.voteCount
228
242
  })]
229
- }), /* @__PURE__ */ h("p", {
243
+ }), /* @__PURE__ */ y("p", {
230
244
  className: "text-[10px] text-muted-foreground",
231
245
  children: "Votes"
232
246
  })]
233
247
  }),
234
- /* @__PURE__ */ g("div", {
248
+ /* @__PURE__ */ b("div", {
235
249
  className: "text-center",
236
- children: [/* @__PURE__ */ g("div", {
250
+ children: [/* @__PURE__ */ b("div", {
237
251
  className: "flex items-center gap-1",
238
- children: [/* @__PURE__ */ h(u, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ h("span", {
252
+ children: [/* @__PURE__ */ y(m, { className: "h-3 w-3 text-yellow-500" }), /* @__PURE__ */ y("span", {
239
253
  className: "text-sm font-bold",
240
254
  children: e.businessValue ?? 0
241
255
  })]
242
- }), /* @__PURE__ */ h("p", {
256
+ }), /* @__PURE__ */ y("p", {
243
257
  className: "text-[10px] text-muted-foreground",
244
258
  children: "Value"
245
259
  })]
@@ -250,14 +264,14 @@ function C() {
250
264
  })
251
265
  }, e.id))
252
266
  }),
253
- N.length === 0 && /* @__PURE__ */ h("p", {
267
+ z.length === 0 && /* @__PURE__ */ y("p", {
254
268
  className: "text-center text-muted-foreground py-8",
255
- children: "No features match the selected filters."
269
+ children: O === "TRIAGE" ? "Nothing waits for triage." : "No features match the selected filters."
256
270
  })
257
271
  ]
258
272
  });
259
273
  }
260
274
  //#endregion
261
- export { C as ProductsFeaturesPagePage };
275
+ export { D as ProductsFeaturesPagePage };
262
276
 
263
277
  //# sourceMappingURL=ProductsFeaturesPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: PlanmagnetFeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n const [sortBy, setSortBy] = useState<'priorityScore' | 'voteCount' | 'businessValue'>(\n 'priorityScore'\n );\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n filter: statusFilter !== 'all' ? { status: statusFilter } : undefined,\n pagination: { first: 50 },\n },\n skip: !workspaceId,\n });\n\n // 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 features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = [...features].sort((a, b) => {\n const aVal = a[sortBy] ?? 0;\n const bVal = b[sortBy] ?? 0;\n return (bVal as number) - (aVal as number);\n });\n\n const statuses = [\n 'all',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n const stats = {\n total: totalCount,\n inProgress: features.filter((f) => f.status === 'IN_PROGRESS').length,\n completed: features.filter((f) => f.status === 'COMPLETED').length,\n avgPriority:\n features.length > 0\n ? Math.round(features.reduce((s, f) => s + (f.priorityScore ?? 0), 0) / features.length)\n : 0,\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n <p className=\"text-sm text-muted-foreground\">\n {totalCount} feature requests across all products\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/new-feature-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Avg Priority Score</p>\n <p className=\"text-xl font-bold\">{stats.avgPriority}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all' ? 'All' : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['priorityScore', 'voteCount', 'businessValue'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'priorityScore' ? 'Priority' : s === 'voteCount' ? 'Votes' : 'Value'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card\n key={feature.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/feature-detail-page', feature.id)),\n feature.title\n )}\n >\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n {/* By name, not user UUID (BOFF-7950). */}\n <span>\n Submitted by: <PersonName id={feature.submittedBy} />\n </span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n No features match the selected filters.\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsFeaturesPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsFeaturesPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport {\n Star,\n Plus,\n ThumbsUp,\n MessageSquare,\n Target,\n Filter,\n ArrowUpDown,\n Loader2,\n} from 'lucide-react';\nimport { countLabel, useFeatureCounts } from '../hooks/useCounts';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { PersonName } from '../people/People';\nimport { FeatureTriageActions, isSnoozed } from '../controls/FeatureTriage';\n\nconst LIST_FEATURE_REQUESTS = gql`\n query ListFeatureRequests(\n $workspaceId: ID!\n $filter: PlanmagnetFeatureRequestFilter\n $pagination: PlanmagnetPaginationInput\n $orderBy: PlanmagnetFeatureOrder\n ) {\n planmagnetFeatureRequests(\n workspaceId: $workspaceId\n filter: $filter\n pagination: $pagination\n orderBy: $orderBy\n ) {\n items {\n id\n title\n description\n status\n businessValue\n effortEstimate\n priorityScore\n voteCount\n submittedBy\n tags\n projectId\n snoozedUntil\n createdAt\n updatedAt\n }\n totalCount\n }\n }\n`;\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n businessValue: number | null;\n effortEstimate: string | null;\n priorityScore: number | null;\n voteCount: number;\n submittedBy: string;\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\nconst statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n DUPLICATE: 'bg-orange-100 text-orange-800',\n};\n\nexport function ProductsFeaturesPagePage() {\n const navigate = usePlanMagnetNavigate();\n const { workspaceId } = usePlanMagnet();\n const [statusFilter, setStatusFilter] = useState<string>('all');\n // Snooze state is read against the moment the page opened (Date.now() may not run during render).\n const [now] = useState(() => Date.now());\n // Sorting happens on the server over every request (BOFF-7965: it re-sorted only the 50 loaded rows).\n const [sortBy, setSortBy] = useState<'NEWEST' | 'MOST_VOTED'>('NEWEST');\n const counts = useFeatureCounts();\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS, {\n variables: {\n workspaceId,\n // Triage (P1.3): the queue and the snoozed requests are server filters, like the status tabs.\n filter:\n statusFilter === 'TRIAGE'\n ? { triage: true }\n : statusFilter === 'SNOOZED'\n ? { snoozed: true }\n : statusFilter !== 'all'\n ? { status: statusFilter }\n : undefined,\n pagination: { first: 50 },\n orderBy: sortBy,\n },\n skip: !workspaceId,\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 features: {error.message}</div>;\n }\n\n const features: FeatureRequestNode[] = data?.planmagnetFeatureRequests?.items ?? [];\n const totalCount = data?.planmagnetFeatureRequests?.totalCount ?? 0;\n\n const sorted = features;\n\n const statuses = [\n 'all',\n 'TRIAGE',\n 'SNOOZED',\n 'SUBMITTED',\n 'UNDER_REVIEW',\n 'PLANNED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DECLINED',\n 'DUPLICATE',\n ];\n\n // Tiles are unfiltered server counts; the tabs change only the list (BOFF-7965).\n const stats = {\n total: countLabel(counts?.all),\n open: countLabel(counts?.open),\n inProgress: countLabel(counts?.byStatus.IN_PROGRESS),\n completed: countLabel(counts?.byStatus.COMPLETED),\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Features</h1>\n {/* Under a tab, the count is of what matches it (BOFF-7990). */}\n <p className=\"text-sm text-muted-foreground\">\n {statusFilter === 'all'\n ? `${stats.total} feature requests`\n : statusFilter === 'TRIAGE'\n ? `${totalCount} waiting for triage`\n : `${totalCount} ${statusFilter.toLowerCase().replace('_', ' ')} match`}\n </p>\n </div>\n <Button size=\"sm\" onClick={() => navigate('/products/new-feature-page')}>\n <Plus className=\"mr-1 h-4 w-4\" />\n New Feature\n </Button>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Total Features</p>\n <p className=\"text-xl font-bold\">{stats.total}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">In Progress</p>\n <p className=\"text-xl font-bold\">{stats.inProgress}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n <p className=\"text-xl font-bold\">{stats.completed}</p>\n </CardContent>\n </Card>\n <Card>\n <CardContent className=\"pt-4\">\n <p className=\"text-xs text-muted-foreground\">Open</p>\n <p className=\"text-xl font-bold\">{stats.open}</p>\n </CardContent>\n </Card>\n </div>\n\n <div className=\"flex items-center justify-between flex-wrap gap-2\">\n <div className=\"flex items-center gap-2\">\n <Filter className=\"h-4 w-4 text-muted-foreground\" />\n {statuses.map((s) => (\n <button\n key={s}\n onClick={() => setStatusFilter(s)}\n className={`px-2 py-1 text-xs rounded-md capitalize ${statusFilter === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'all'\n ? 'All'\n : s === 'TRIAGE'\n ? `Triage (${countLabel(counts?.triage)})`\n : s === 'SNOOZED'\n ? `Snoozed (${countLabel(counts?.snoozed)})`\n : s.toLowerCase().replace('_', ' ')}\n </button>\n ))}\n </div>\n <div className=\"flex items-center gap-2\">\n <ArrowUpDown className=\"h-4 w-4 text-muted-foreground\" />\n {(['NEWEST', 'MOST_VOTED'] as const).map((s) => (\n <button\n key={s}\n onClick={() => setSortBy(s)}\n className={`px-2 py-1 text-xs rounded-md ${sortBy === s ? 'bg-primary text-primary-foreground' : 'bg-muted text-muted-foreground'}`}\n >\n {s === 'NEWEST' ? 'Newest' : 'Most voted'}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-3\">\n {sorted.map((feature) => (\n <Card\n key={feature.id}\n {...rowLinkProps(\n () => navigate(detailPath('/products/feature-detail-page', feature.id)),\n feature.title\n )}\n >\n <CardContent className=\"py-4\">\n <div className=\"flex items-start justify-between gap-4\">\n <div className=\"flex-1 space-y-2\">\n <div className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium\">{feature.title}</span>\n <span\n className={`text-[10px] px-1.5 py-0.5 rounded-full capitalize ${statusColor[feature.status] ?? 'bg-gray-100'}`}\n >\n {feature.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\n <p className=\"text-xs text-muted-foreground\">{feature.description}</p>\n <div className=\"flex items-center gap-4 text-xs text-muted-foreground\">\n <span>Effort: {feature.effortEstimate ?? 'N/A'}</span>\n {/* By name, not user UUID (BOFF-7950). */}\n <span>\n Submitted by: <PersonName id={feature.submittedBy} />\n </span>\n <span>{new Date(feature.updatedAt).toLocaleDateString()}</span>\n </div>\n {isSnoozed(feature.snoozedUntil, now) && (\n <p className=\"text-xs text-muted-foreground\">\n Snoozed until {new Date(feature.snoozedUntil).toLocaleDateString()}\n </p>\n )}\n <FeatureTriageActions feature={feature} now={now} />\n {feature.tags.length > 0 && (\n <div className=\"flex gap-1\">\n {feature.tags.map((tag) => (\n <Badge key={tag} variant=\"outline\" className=\"text-[10px]\">\n {tag}\n </Badge>\n ))}\n </div>\n )}\n </div>\n <div className=\"flex items-center gap-4 shrink-0\">\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Target className=\"h-3 w-3 text-primary\" />\n <span className=\"text-sm font-bold\">{feature.priorityScore ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Priority</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <ThumbsUp className=\"h-3 w-3 text-green-500\" />\n <span className=\"text-sm font-bold\">{feature.voteCount}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Votes</p>\n </div>\n <div className=\"text-center\">\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-yellow-500\" />\n <span className=\"text-sm font-bold\">{feature.businessValue ?? 0}</span>\n </div>\n <p className=\"text-[10px] text-muted-foreground\">Value</p>\n </div>\n </div>\n </div>\n </CardContent>\n </Card>\n ))}\n </div>\n {sorted.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">\n {statusFilter === 'TRIAGE'\n ? 'Nothing waits for triage.'\n : 'No features match the selected filters.'}\n </p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"names":[],"mappings":"AAoGA,wBAAgB,6BAA6B,4CA8N5C"}
1
+ {"version":3,"file":"ProductsProductDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"names":[],"mappings":"AAoGA,wBAAgB,6BAA6B,4CAmO5C"}
@@ -127,12 +127,12 @@ function C() {
127
127
  icon: c
128
128
  },
129
129
  {
130
- label: "Releases",
130
+ label: "Releases in workspace",
131
131
  value: P,
132
132
  icon: a
133
133
  },
134
134
  {
135
- label: "Features",
135
+ label: "Feature requests in workspace",
136
136
  value: N,
137
137
  icon: s
138
138
  },
@@ -156,11 +156,7 @@ function C() {
156
156
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
157
157
  children: [/* @__PURE__ */ m("div", {
158
158
  className: "lg:col-span-2 space-y-6",
159
- children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, { children: [
160
- "Feature Requests (",
161
- j.length,
162
- ")"
163
- ] }) }), /* @__PURE__ */ m(_, {
159
+ children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ m(y, { children: ["Latest feature requests in the workspace", N > j.length ? ` (${j.length} of ${N})` : ""] }) }), /* @__PURE__ */ m(_, {
164
160
  className: "space-y-3",
165
161
  children: [j.length === 0 && /* @__PURE__ */ p("p", {
166
162
  className: "text-sm text-muted-foreground",
@@ -217,7 +213,7 @@ function C() {
217
213
  })] })]
218
214
  }), /* @__PURE__ */ m("div", {
219
215
  className: "space-y-6",
220
- children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Recent Releases" }) }), /* @__PURE__ */ m(_, {
216
+ children: [/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Latest releases in the workspace" }) }), /* @__PURE__ */ m(_, {
221
217
  className: "space-y-3",
222
218
  children: [M.length === 0 && /* @__PURE__ */ p("p", {
223
219
  className: "text-sm text-muted-foreground",
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Package, GitBranch, Map, Star, Users, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport { PlanningFocusBadge } from '../roadmaps/planningFocus';\nimport { RoadmapHeaderActions } from '../roadmaps/RoadmapControls';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetail($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_PRODUCT = gql`\n query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n title\n description\n status\n priorityScore\n voteCount\n businessValue\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES_FOR_PRODUCT = gql`\n query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n version\n status\n plannedDate\n }\n totalCount\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n priorityScore: number | null;\n voteCount: number;\n businessValue: number | null;\n}\n\ninterface ReleaseNode {\n id: string;\n name: string;\n version: string;\n status: string;\n plannedDate: string | null;\n}\n\nexport function ProductsProductDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = 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_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const {\n data: featuresData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\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 product: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Product not found.</div>;\n\n const roadmapItems: RoadmapItemNode[] = roadmap.items ?? [];\n const features: FeatureRequestNode[] = featuresData?.planmagnetFeatureRequests?.items ?? [];\n const releases: ReleaseNode[] = releasesData?.planmagnetReleases?.items ?? [];\n const totalFeatures = featuresData?.planmagnetFeatureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.planmagnetReleases?.totalCount ?? 0;\n\n const statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n </div>\n {/* Edit and Delete (BOFF-7941): the Edit page existed and nothing linked to it. */}\n <div className=\"flex flex-col items-end gap-2\">\n <RoadmapHeaderActions roadmapId={roadmap.id} name={roadmap.name} />\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n { label: 'Roadmap Items', value: roadmapItems.length, icon: Star },\n { label: 'Releases', value: totalReleases, icon: GitBranch },\n { label: 'Features', value: totalFeatures, icon: Map },\n // The name, as the Details card below shows it — not the owner's UUID in large type (BOFF-7976).\n { label: 'Owner', value: <PersonName id={roadmap.ownerId} />, icon: Users },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Feature Requests ({features.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {features.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No feature requests yet.</p>\n )}\n {features.map((f) => (\n <div key={f.id} className=\"flex items-center justify-between rounded-md border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{f.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Votes: {f.voteCount} &middot; Business Value: {f.businessValue ?? 'N/A'}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"capitalize text-xs\">\n {f.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Roadmap Items ({roadmapItems.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {roadmapItems.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No roadmap items linked.</p>\n )}\n {roadmapItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {item.startDate\n ? new Date(item.startDate).toLocaleDateString()\n : 'No start date'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'No end date'}\n </p>\n </div>\n <Badge variant=\"outline\" className=\"capitalize text-xs\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Recent Releases</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {releases.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No releases yet.</p>\n )}\n {releases.map((r) => (\n <div key={r.id} className=\"border-b pb-2 last:border-0 last:pb-0\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{r.version}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {r.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{r.name}</p>\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {r.plannedDate ? new Date(r.plannedDate).toLocaleDateString() : 'No date set'}\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span>{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProductsProductDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProductsProductDetailPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge } from '@burdenoff/fe-libs/ui';\nimport { Package, GitBranch, Map, Star, Users, Calendar, Loader2 } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useParams } from 'react-router';\nimport { PersonName } from '../people/People';\nimport { PlanningFocusBadge } from '../roadmaps/planningFocus';\nimport { RoadmapHeaderActions } from '../roadmaps/RoadmapControls';\n\nconst GET_ROADMAP = gql`\n query GetRoadmapDetail($id: ID!) {\n planmagnetRoadmap(id: $id) {\n id\n workspaceId\n name\n description\n type\n planningFocus\n isPublic\n ownerId\n createdAt\n updatedAt\n items {\n id\n title\n description\n status\n startDate\n endDate\n color\n swimlane\n sortOrder\n }\n }\n }\n`;\n\nconst LIST_FEATURE_REQUESTS_FOR_PRODUCT = gql`\n query ListFeatureRequestsForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetFeatureRequests(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n title\n description\n status\n priorityScore\n voteCount\n businessValue\n createdAt\n }\n totalCount\n }\n }\n`;\n\nconst LIST_RELEASES_FOR_PRODUCT = gql`\n query ListReleasesForProduct($workspaceId: ID!, $pagination: PlanmagnetPaginationInput) {\n planmagnetReleases(workspaceId: $workspaceId, pagination: $pagination) {\n items {\n id\n name\n version\n status\n plannedDate\n }\n totalCount\n }\n }\n`;\n\ninterface RoadmapItemNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n}\n\ninterface FeatureRequestNode {\n id: string;\n title: string;\n description: string | null;\n status: string;\n priorityScore: number | null;\n voteCount: number;\n businessValue: number | null;\n}\n\ninterface ReleaseNode {\n id: string;\n name: string;\n version: string;\n status: string;\n plannedDate: string | null;\n}\n\nexport function ProductsProductDetailPagePage() {\n const { workspaceId } = usePlanMagnet();\n const params = useParams<{ id: string }>();\n const roadmapId = 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_ROADMAP, {\n variables: { id: roadmapId },\n skip: !roadmapId,\n });\n\n const {\n data: featuresData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_FEATURE_REQUESTS_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\n });\n\n const {\n data: releasesData,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(LIST_RELEASES_FOR_PRODUCT, {\n variables: { workspaceId, pagination: { first: 20 } },\n skip: !workspaceId,\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 product: {error.message}</div>;\n }\n\n const roadmap = data?.planmagnetRoadmap;\n if (!roadmap) return <div className=\"p-6 text-muted-foreground\">Product not found.</div>;\n\n const roadmapItems: RoadmapItemNode[] = roadmap.items ?? [];\n const features: FeatureRequestNode[] = featuresData?.planmagnetFeatureRequests?.items ?? [];\n const releases: ReleaseNode[] = releasesData?.planmagnetReleases?.items ?? [];\n const totalFeatures = featuresData?.planmagnetFeatureRequests?.totalCount ?? 0;\n const totalReleases = releasesData?.planmagnetReleases?.totalCount ?? 0;\n\n const statusColor: Record<string, string> = {\n SUBMITTED: 'bg-gray-100 text-gray-800',\n UNDER_REVIEW: 'bg-yellow-100 text-yellow-800',\n PLANNED: 'bg-blue-100 text-blue-800',\n IN_PROGRESS: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-green-100 text-green-800',\n DECLINED: 'bg-red-100 text-red-800',\n };\n\n return (\n <div className=\"space-y-6 p-6\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{roadmap.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {roadmap.description ?? 'No description'}\n </p>\n </div>\n </div>\n {/* Edit and Delete (BOFF-7941): the Edit page existed and nothing linked to it. */}\n <div className=\"flex flex-col items-end gap-2\">\n <RoadmapHeaderActions roadmapId={roadmap.id} name={roadmap.name} />\n <PlanningFocusBadge focus={roadmap.planningFocus} />\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n {[\n { label: 'Roadmap Items', value: roadmapItems.length, icon: Star },\n // A product (a roadmap) has no project scope of its own, so these are the WORKSPACE's numbers — said so,\n // rather than shown as if they belonged to the product (BOFF-7991).\n { label: 'Releases in workspace', value: totalReleases, icon: GitBranch },\n { label: 'Feature requests in workspace', value: totalFeatures, icon: Map },\n // The name, as the Details card below shows it — not the owner's UUID in large type (BOFF-7976).\n { label: 'Owner', value: <PersonName id={roadmap.ownerId} />, icon: Users },\n ].map((s) => (\n <Card key={s.label}>\n <CardContent className=\"pt-4\">\n <div className=\"flex items-center gap-2 text-muted-foreground text-sm\">\n <s.icon className=\"h-4 w-4\" />\n {s.label}\n </div>\n <p className=\"text-xl font-semibold mt-1\">{s.value}</p>\n </CardContent>\n </Card>\n ))}\n </div>\n\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>\n Latest feature requests in the workspace\n {totalFeatures > features.length ? ` (${features.length} of ${totalFeatures})` : ''}\n </CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {features.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No feature requests yet.</p>\n )}\n {features.map((f) => (\n <div key={f.id} className=\"flex items-center justify-between rounded-md border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{f.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n Votes: {f.voteCount} &middot; Business Value: {f.businessValue ?? 'N/A'}\n </p>\n </div>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\" className=\"capitalize text-xs\">\n {f.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Roadmap Items ({roadmapItems.length})</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {roadmapItems.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No roadmap items linked.</p>\n )}\n {roadmapItems.map((item) => (\n <div\n key={item.id}\n className=\"flex items-center justify-between rounded-md border p-3\"\n >\n <div>\n <p className=\"text-sm font-medium\">{item.title}</p>\n <p className=\"text-xs text-muted-foreground\">\n {item.startDate\n ? new Date(item.startDate).toLocaleDateString()\n : 'No start date'}\n {' - '}\n {item.endDate ? new Date(item.endDate).toLocaleDateString() : 'No end date'}\n </p>\n </div>\n <Badge variant=\"outline\" className=\"capitalize text-xs\">\n {item.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Latest releases in the workspace</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {releases.length === 0 && (\n <p className=\"text-sm text-muted-foreground\">No releases yet.</p>\n )}\n {releases.map((r) => (\n <div key={r.id} className=\"border-b pb-2 last:border-0 last:pb-0\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm font-medium\">{r.version}</span>\n <Badge variant=\"outline\" className=\"text-xs capitalize\">\n {r.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n <p className=\"text-xs text-muted-foreground\">{r.name}</p>\n <div className=\"flex items-center gap-1 mt-1 text-xs text-muted-foreground\">\n <Calendar className=\"h-3 w-3\" />\n {r.plannedDate ? new Date(r.plannedDate).toLocaleDateString() : 'No date set'}\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created</span>\n <span>{new Date(roadmap.createdAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Updated</span>\n <span>{new Date(roadmap.updatedAt).toLocaleDateString()}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Owner</span>\n <span>\n <PersonName id={roadmap.ownerId} />\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Visibility</span>\n <span>{roadmap.isPublic ? 'Public' : 'Private'}</span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAYA,IAAM,IAAc,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BjB,IAAoC,CAAG;;;;;;;;;;;;;;;;GAkBvC,IAA4B,CAAG;;;;;;;;;;;;;;AA0CrC,SAAgB,IAAgC;CAC9C,IAAM,EAAE,mBAAgB,GAAe,EAEjC,IADS,GAA2B,CACjB,MAAM,IAEzB,EACJ,SACA,YACA,aAGA,EAA8B,GAAa;EACzC,WAAW,EAAE,IAAI,GAAW;EAC5B,MAAM,CAAC;EACR,CAAC,EAEE,EACJ,MAAM,MAGN,EAA8B,GAAmC;EAC/D,WAAW;GAAE;GAAa,YAAY,EAAE,OAAO,IAAI;GAAE;EACrD,MAAM,CAAC;EACR,CAAC,EAEE,EACJ,MAAM,MAGN,EAA8B,GAA2B;EACvD,WAAW;GAAE;GAAa,YAAY,EAAE,OAAO,IAAI;GAAE;EACrD,MAAM,CAAC;EACR,CAAC;AAMJ,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,8CAA+C,CAAA;EAC9D,CAAA;AAIV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAsC,4BAAyB,EAAM,QAAc;;CAG5F,IAAM,IAAU,GAAM;AACtB,KAAI,CAAC,EAAS,QAAO,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAAwB,CAAA;CAExF,IAAM,IAAkC,EAAQ,SAAS,EAAE,EACrD,IAAiC,GAAc,2BAA2B,SAAS,EAAE,EACrF,IAA0B,GAAc,oBAAoB,SAAS,EAAE,EACvE,IAAgB,GAAc,2BAA2B,cAAc,GACvE,IAAgB,GAAc,oBAAoB,cAAc;AAWtE,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA0B,EAAQ;MAAU,CAAA,EAC1D,kBAAC,KAAD;MAAG,WAAU;gBACV,EAAQ,eAAe;MACtB,CAAA,CACA,EAAA,CAAA;KACF,CAAA,EAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MAAsB,WAAW,EAAQ;MAAI,MAAM,EAAQ;MAAQ,CAAA,EACnE,kBAAC,GAAD,EAAoB,OAAO,EAAQ,eAAiB,CAAA,CAChD;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ;KACC;MAAE,OAAO;MAAiB,OAAO,EAAa;MAAQ,MAAM;MAAM;KAGlE;MAAE,OAAO;MAAyB,OAAO;MAAe,MAAM;MAAW;KACzE;MAAE,OAAO;MAAiC,OAAO;MAAe,MAAM;MAAK;KAE3E;MAAE,OAAO;MAAS,OAAO,kBAAC,GAAD,EAAY,IAAI,EAAQ,SAAW,CAAA;MAAE,MAAM;MAAO;KAC5E,CAAC,KAAK,MACL,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;KAAa,WAAU;eAAvB,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,EAAE,MAAH,EAAQ,WAAU,WAAY,CAAA,EAC7B,EAAE,MACC;SACN,kBAAC,KAAD;MAAG,WAAU;gBAA8B,EAAE;MAAU,CAAA,CAC3C;QACT,EARI,EAAE,MAQN,CACP;IACE,CAAA;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA,CAAW,4CAER,IAAgB,EAAS,SAAS,KAAK,EAAS,OAAO,MAAM,EAAc,KAAK,GACvE,EAAA,CAAA,EACD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACG,EAAS,WAAW,KACnB,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAA4B,CAAA,EAE1E,EAAS,KAAK,MACb,kBAAC,OAAD;OAAgB,WAAU;iBAA1B,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuB,EAAE;QAAU,CAAA,EAChD,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAA6C;SACnC,EAAE;SAAU;SAA2B,EAAE,iBAAiB;SAChE;UACA,EAAA,CAAA,EACN,kBAAC,OAAD;QAAK,WAAU;kBACb,kBAAC,GAAD;SAAO,SAAQ;SAAY,WAAU;mBAClC,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;SACnC,CAAA;QACJ,CAAA,CACF;SAZI,EAAE,GAYN,CACN,CACU;QACT,EAAA,CAAA,EAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;MAAW;MAAgB,EAAa;MAAO;MAAa,EAAA,CAAA,EACjD,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACG,EAAa,WAAW,KACvB,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAA4B,CAAA,EAE1E,EAAa,KAAK,MACjB,kBAAC,OAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBAAuB,EAAK;QAAU,CAAA,EACnD,kBAAC,KAAD;QAAG,WAAU;kBAAb;SACG,EAAK,YACF,IAAI,KAAK,EAAK,UAAU,CAAC,oBAAoB,GAC7C;SACH;SACA,EAAK,UAAU,IAAI,KAAK,EAAK,QAAQ,CAAC,oBAAoB,GAAG;SAC5D;UACA,EAAA,CAAA,EACN,kBAAC,GAAD;QAAO,SAAQ;QAAU,WAAU;kBAChC,EAAK,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;QACtC,CAAA,CACJ;SAhBC,EAAK,GAgBN,CACN,CACU;QACT,EAAA,CAAA,CACH;QAEN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,oCAA4C,CAAA,EAC5C,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACG,EAAS,WAAW,KACnB,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAoB,CAAA,EAElE,EAAS,KAAK,MACb,kBAAC,OAAD;OAAgB,WAAU;iBAA1B;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,QAAD;UAAM,WAAU;oBAAuB,EAAE;UAAe,CAAA,EACxD,kBAAC,GAAD;UAAO,SAAQ;UAAU,WAAU;oBAChC,EAAE,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;UACnC,CAAA,CACJ;;QACN,kBAAC,KAAD;SAAG,WAAU;mBAAiC,EAAE;SAAS,CAAA;QACzD,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA,EAC/B,EAAE,cAAc,IAAI,KAAK,EAAE,YAAY,CAAC,oBAAoB,GAAG,cAC5D;;QACF;SAZI,EAAE,GAYN,CACN,CACU;QACT,EAAA,CAAA,EAEP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,WAAmB,CAAA,EACnB,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB;OACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC3D;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAc,CAAA,EACtD,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA,CAC3D;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAY,CAAA,EACpD,kBAAC,QAAD,EAAA,UACE,kBAAC,GAAD,EAAY,IAAI,EAAQ,SAAW,CAAA,EAC9B,CAAA,CACH;;OACN,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,QAAD;SAAM,WAAU;mBAAwB;SAAiB,CAAA,EACzD,kBAAC,QAAD,EAAA,UAAO,EAAQ,WAAW,WAAW,WAAiB,CAAA,CAClD;;OACM;QACT,EAAA,CAAA,CACH;OACF;;GACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,wBAAwB,4CAkLvC"}
1
+ {"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,wBAAwB,4CA+KvC"}