@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.
- package/dist/generated/global-types.d.ts +9 -1
- package/dist/generated/global-types.d.ts.map +1 -1
- package/dist/generated/wspace-operations.d.ts +0 -12
- package/dist/generated/wspace-operations.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +14 -10
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +149 -127
- package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +142 -139
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/FeatureTriage.d.ts +21 -0
- package/dist/planmagnet/controls/FeatureTriage.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeatureTriage.js +398 -0
- package/dist/planmagnet/controls/FeatureTriage.js.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/MilestoneControls.js +76 -73
- package/dist/planmagnet/controls/MilestoneControls.js.map +1 -1
- package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/ProjectControls.js +47 -44
- package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
- package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/ReleaseControls.js +47 -44
- package/dist/planmagnet/controls/ReleaseControls.js.map +1 -1
- package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/WorkItemControls.js +68 -65
- package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
- package/dist/planmagnet/hooks/useCounts.d.ts +39 -0
- package/dist/planmagnet/hooks/useCounts.d.ts.map +1 -0
- package/dist/planmagnet/hooks/useCounts.js +209 -0
- package/dist/planmagnet/hooks/useCounts.js.map +1 -0
- package/dist/planmagnet/import/exportColumns.d.ts +52 -0
- package/dist/planmagnet/import/exportColumns.d.ts.map +1 -0
- package/dist/planmagnet/import/exportColumns.js +81 -0
- package/dist/planmagnet/import/exportColumns.js.map +1 -0
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +255 -242
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +199 -160
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +147 -144
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js +2 -7
- package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +173 -173
- package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +120 -117
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js +97 -117
- package/dist/planmagnet/pages/ProductsFeatureAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +168 -103
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +117 -103
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +4 -8
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +85 -90
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +12 -2
- package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js +2 -2
- package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +48 -71
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +17 -13
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +1 -1
- package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +6 -6
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +11 -8
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +4 -4
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +152 -139
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +75 -74
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts +7 -0
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +39 -201
- package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +118 -99
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksImportPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksImportPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksImportPagePage.js +309 -0
- package/dist/planmagnet/pages/TasksImportPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +110 -104
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +99 -177
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -4
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.d.ts.map +1 -1
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.js +10 -4
- package/dist/planmagnet/releases/ReleaseWorkItemsCard.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +151 -138
- package/dist/planmagnet/roadmaps/RoadmapControls.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.js +32 -30
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
- package/dist/planmagnet/utils/mutationResult.d.ts +10 -0
- package/dist/planmagnet/utils/mutationResult.d.ts.map +1 -1
- package/dist/planmagnet/utils/mutationResult.js +2 -1
- package/dist/planmagnet/utils/mutationResult.js.map +1 -1
- package/dist/planmagnet/utils/rowNavigation.d.ts +2 -0
- package/dist/planmagnet/utils/rowNavigation.d.ts.map +1 -1
- package/dist/planmagnet/utils/rowNavigation.js +9 -8
- package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
- 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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
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
|
|
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(
|
|
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
|
-
`,
|
|
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
|
|
45
|
-
let
|
|
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:
|
|
48
|
-
filter:
|
|
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: !
|
|
62
|
+
skip: !D
|
|
52
63
|
});
|
|
53
|
-
if (
|
|
64
|
+
if (F && !P) return /* @__PURE__ */ y("div", {
|
|
54
65
|
className: "flex items-center justify-center p-12",
|
|
55
|
-
children: /* @__PURE__ */
|
|
66
|
+
children: /* @__PURE__ */ y(f, { className: "h-8 w-8 animate-spin text-muted-foreground" })
|
|
56
67
|
});
|
|
57
|
-
if (
|
|
68
|
+
if (I) return /* @__PURE__ */ b("div", {
|
|
58
69
|
className: "p-6 text-destructive",
|
|
59
|
-
children: ["Failed to load features: ",
|
|
70
|
+
children: ["Failed to load features: ", I.message]
|
|
60
71
|
});
|
|
61
|
-
let
|
|
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
|
-
],
|
|
74
|
-
total:
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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__ */
|
|
89
|
+
return /* @__PURE__ */ b("div", {
|
|
80
90
|
className: "space-y-6 p-6",
|
|
81
91
|
children: [
|
|
82
|
-
/* @__PURE__ */
|
|
92
|
+
/* @__PURE__ */ b("div", {
|
|
83
93
|
className: "flex items-center justify-between",
|
|
84
|
-
children: [/* @__PURE__ */
|
|
94
|
+
children: [/* @__PURE__ */ b("div", { children: [/* @__PURE__ */ y("h1", {
|
|
85
95
|
className: "text-2xl font-semibold",
|
|
86
96
|
children: "Features"
|
|
87
|
-
}), /* @__PURE__ */
|
|
97
|
+
}), /* @__PURE__ */ y("p", {
|
|
88
98
|
className: "text-sm text-muted-foreground",
|
|
89
|
-
children:
|
|
90
|
-
})] }), /* @__PURE__ */
|
|
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: () =>
|
|
93
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
106
|
+
/* @__PURE__ */ b("div", {
|
|
97
107
|
className: "grid grid-cols-2 md:grid-cols-4 gap-4",
|
|
98
108
|
children: [
|
|
99
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
|
|
100
110
|
className: "pt-4",
|
|
101
|
-
children: [/* @__PURE__ */
|
|
111
|
+
children: [/* @__PURE__ */ y("p", {
|
|
102
112
|
className: "text-xs text-muted-foreground",
|
|
103
113
|
children: "Total Features"
|
|
104
|
-
}), /* @__PURE__ */
|
|
114
|
+
}), /* @__PURE__ */ y("p", {
|
|
105
115
|
className: "text-xl font-bold",
|
|
106
|
-
children:
|
|
116
|
+
children: V.total
|
|
107
117
|
})]
|
|
108
118
|
}) }),
|
|
109
|
-
/* @__PURE__ */
|
|
119
|
+
/* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
|
|
110
120
|
className: "pt-4",
|
|
111
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ y("p", {
|
|
112
122
|
className: "text-xs text-muted-foreground",
|
|
113
123
|
children: "In Progress"
|
|
114
|
-
}), /* @__PURE__ */
|
|
124
|
+
}), /* @__PURE__ */ y("p", {
|
|
115
125
|
className: "text-xl font-bold",
|
|
116
|
-
children:
|
|
126
|
+
children: V.inProgress
|
|
117
127
|
})]
|
|
118
128
|
}) }),
|
|
119
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
|
|
120
130
|
className: "pt-4",
|
|
121
|
-
children: [/* @__PURE__ */
|
|
131
|
+
children: [/* @__PURE__ */ y("p", {
|
|
122
132
|
className: "text-xs text-muted-foreground",
|
|
123
133
|
children: "Completed"
|
|
124
|
-
}), /* @__PURE__ */
|
|
134
|
+
}), /* @__PURE__ */ y("p", {
|
|
125
135
|
className: "text-xl font-bold",
|
|
126
|
-
children:
|
|
136
|
+
children: V.completed
|
|
127
137
|
})]
|
|
128
138
|
}) }),
|
|
129
|
-
/* @__PURE__ */
|
|
139
|
+
/* @__PURE__ */ y(C, { children: /* @__PURE__ */ b(w, {
|
|
130
140
|
className: "pt-4",
|
|
131
|
-
children: [/* @__PURE__ */
|
|
141
|
+
children: [/* @__PURE__ */ y("p", {
|
|
132
142
|
className: "text-xs text-muted-foreground",
|
|
133
|
-
children: "
|
|
134
|
-
}), /* @__PURE__ */
|
|
143
|
+
children: "Open"
|
|
144
|
+
}), /* @__PURE__ */ y("p", {
|
|
135
145
|
className: "text-xl font-bold",
|
|
136
|
-
children:
|
|
146
|
+
children: V.open
|
|
137
147
|
})]
|
|
138
148
|
}) })
|
|
139
149
|
]
|
|
140
150
|
}),
|
|
141
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ b("div", {
|
|
142
152
|
className: "flex items-center justify-between flex-wrap gap-2",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
153
|
+
children: [/* @__PURE__ */ b("div", {
|
|
144
154
|
className: "flex items-center gap-2",
|
|
145
|
-
children: [/* @__PURE__ */
|
|
146
|
-
onClick: () =>
|
|
147
|
-
className: `px-2 py-1 text-xs rounded-md capitalize ${
|
|
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__ */
|
|
160
|
+
}), /* @__PURE__ */ b("div", {
|
|
151
161
|
className: "flex items-center gap-2",
|
|
152
|
-
children: [/* @__PURE__ */
|
|
153
|
-
|
|
154
|
-
"
|
|
155
|
-
"
|
|
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__ */
|
|
169
|
+
/* @__PURE__ */ y("div", {
|
|
164
170
|
className: "space-y-3",
|
|
165
|
-
children:
|
|
166
|
-
...
|
|
167
|
-
children: /* @__PURE__ */
|
|
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__ */
|
|
175
|
+
children: /* @__PURE__ */ b("div", {
|
|
170
176
|
className: "flex items-start justify-between gap-4",
|
|
171
|
-
children: [/* @__PURE__ */
|
|
177
|
+
children: [/* @__PURE__ */ b("div", {
|
|
172
178
|
className: "flex-1 space-y-2",
|
|
173
179
|
children: [
|
|
174
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ b("div", {
|
|
175
181
|
className: "flex items-center gap-2",
|
|
176
|
-
children: [/* @__PURE__ */
|
|
182
|
+
children: [/* @__PURE__ */ y("span", {
|
|
177
183
|
className: "text-sm font-medium",
|
|
178
184
|
children: e.title
|
|
179
|
-
}), /* @__PURE__ */
|
|
180
|
-
className: `text-[10px] px-1.5 py-0.5 rounded-full capitalize ${
|
|
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__ */
|
|
190
|
+
/* @__PURE__ */ y("p", {
|
|
185
191
|
className: "text-xs text-muted-foreground",
|
|
186
192
|
children: e.description
|
|
187
193
|
}),
|
|
188
|
-
/* @__PURE__ */
|
|
194
|
+
/* @__PURE__ */ b("div", {
|
|
189
195
|
className: "flex items-center gap-4 text-xs text-muted-foreground",
|
|
190
196
|
children: [
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
/* @__PURE__ */
|
|
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.
|
|
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__ */
|
|
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__ */
|
|
219
|
+
}), /* @__PURE__ */ b("div", {
|
|
206
220
|
className: "flex items-center gap-4 shrink-0",
|
|
207
221
|
children: [
|
|
208
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ b("div", {
|
|
209
223
|
className: "text-center",
|
|
210
|
-
children: [/* @__PURE__ */
|
|
224
|
+
children: [/* @__PURE__ */ b("div", {
|
|
211
225
|
className: "flex items-center gap-1",
|
|
212
|
-
children: [/* @__PURE__ */ h
|
|
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__ */
|
|
230
|
+
}), /* @__PURE__ */ y("p", {
|
|
217
231
|
className: "text-[10px] text-muted-foreground",
|
|
218
232
|
children: "Priority"
|
|
219
233
|
})]
|
|
220
234
|
}),
|
|
221
|
-
/* @__PURE__ */
|
|
235
|
+
/* @__PURE__ */ b("div", {
|
|
222
236
|
className: "text-center",
|
|
223
|
-
children: [/* @__PURE__ */
|
|
237
|
+
children: [/* @__PURE__ */ b("div", {
|
|
224
238
|
className: "flex items-center gap-1",
|
|
225
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
243
|
+
}), /* @__PURE__ */ y("p", {
|
|
230
244
|
className: "text-[10px] text-muted-foreground",
|
|
231
245
|
children: "Votes"
|
|
232
246
|
})]
|
|
233
247
|
}),
|
|
234
|
-
/* @__PURE__ */
|
|
248
|
+
/* @__PURE__ */ b("div", {
|
|
235
249
|
className: "text-center",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
250
|
+
children: [/* @__PURE__ */ b("div", {
|
|
237
251
|
className: "flex items-center gap-1",
|
|
238
|
-
children: [/* @__PURE__ */
|
|
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__ */
|
|
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
|
-
|
|
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 {
|
|
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":";;;;;;;;;;AAoBA,IAAM,IAAwB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;GAyC3B,IAAsC;CAC1C,WAAW;CACX,cAAc;CACd,SAAS;CACT,aAAa;CACb,WAAW;CACX,UAAU;CACV,WAAW;CACZ;AAED,SAAgB,IAA2B;CACzC,IAAM,IAAW,GAAuB,EAClC,EAAE,mBAAgB,GAAe,EACjC,CAAC,GAAc,KAAmB,EAAiB,MAAM,EACzD,CAAC,GAAQ,KAAa,EAC1B,gBACD,EAEK,EACJ,SACA,YACA,aAGA,EAA8B,GAAuB;EACnD,WAAW;GACT;GACA,QAAQ,MAAiB,QAAmC,KAAA,IAA3B,EAAE,QAAQ,GAAc;GACzD,YAAY,EAAE,OAAO,IAAI;GAC1B;EACD,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,6BAA0B,EAAM,QAAc;;CAG7F,IAAM,IAAiC,GAAM,2BAA2B,SAAS,EAAE,EAC7E,IAAa,GAAM,2BAA2B,cAAc,GAE5D,IAAS,CAAC,GAAG,EAAS,CAAC,MAAM,GAAG,MAAM;EAC1C,IAAM,IAAO,EAAE,MAAW;AAE1B,UADa,EAAE,MAAW,KACC;GAC3B,EAEI,IAAW;EACf;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,EAEK,IAAQ;EACZ,OAAO;EACP,YAAY,EAAS,QAAQ,MAAM,EAAE,WAAW,cAAc,CAAC;EAC/D,WAAW,EAAS,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC;EAC5D,aACE,EAAS,SAAS,IACd,KAAK,MAAM,EAAS,QAAQ,GAAG,MAAM,KAAK,EAAE,iBAAiB,IAAI,EAAE,GAAG,EAAS,OAAO,GACtF;EACP;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAa,CAAA,EACpD,kBAAC,KAAD;KAAG,WAAU;eAAb,CACG,GAAW,wCACV;OACA,EAAA,CAAA,EACN,kBAAC,GAAD;KAAQ,MAAK;KAAK,eAAe,EAAS,6BAA6B;eAAvE,CACE,kBAAC,GAAD,EAAM,WAAU,gBAAiB,CAAA,EAAA,cAE1B;OACL;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAkB,CAAA,EAC/D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAU,CAAA,CACtC;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAe,CAAA,EAC5D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAe,CAAA,CAC3C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAa,CAAA,EAC1D,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAc,CAAA,CAC1C;SACT,CAAA;KACP,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAAsB,CAAA,EACnE,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAM;OAAgB,CAAA,CAC5C;SACT,CAAA;KACH;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,iCAAkC,CAAA,EACnD,EAAS,KAAK,MACb,kBAAC,UAAD;MAEE,eAAe,EAAgB,EAAE;MACjC,WAAW,2CAA2C,MAAiB,IAAI,uCAAuC;gBAEjH,MAAM,QAAQ,QAAQ,EAAE,aAAa,CAAC,QAAQ,KAAK,IAAI;MACjD,EALF,EAKE,CACT,CACE;QACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAa,WAAU,iCAAkC,CAAA,EACvD;MAAC;MAAiB;MAAa;MAAgB,CAAW,KAAK,MAC/D,kBAAC,UAAD;MAEE,eAAe,EAAU,EAAE;MAC3B,WAAW,2CAA2C,MAAW,IAAI,uCAAuC;gBAE3G,MAAM,kBAAkB,aAAa,MAAM,cAAc,UAAU;MAC7D,EALF,EAKE,CACT,CACE;OACF;;GAEN,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,MACX,kBAAC,GAAD;KAEE,GAAI,QACI,EAAS,EAAW,iCAAiC,EAAQ,GAAG,CAAC,EACvE,EAAQ,MACT;eAED,kBAAC,GAAD;MAAa,WAAU;gBACrB,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBAAuB,EAAQ;WAAa,CAAA,EAC5D,kBAAC,QAAD;WACE,WAAW,qDAAqD,EAAY,EAAQ,WAAW;qBAE9F,EAAQ,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;WAC1C,CAAA,CACH;;SACN,kBAAC,KAAD;UAAG,WAAU;oBAAiC,EAAQ;UAAgB,CAAA;SACtE,kBAAC,OAAD;UAAK,WAAU;oBAAf;WACE,kBAAC,QAAD,EAAA,UAAA,CAAM,YAAS,EAAQ,kBAAkB,MAAa,EAAA,CAAA;WAEtD,kBAAC,QAAD,EAAA,UAAA,CAAM,kBACU,kBAAC,GAAD,EAAY,IAAI,EAAQ,aAAe,CAAA,CAChD,EAAA,CAAA;WACP,kBAAC,QAAD,EAAA,UAAO,IAAI,KAAK,EAAQ,UAAU,CAAC,oBAAoB,EAAQ,CAAA;WAC3D;;SACL,EAAQ,KAAK,SAAS,KACrB,kBAAC,OAAD;UAAK,WAAU;oBACZ,EAAQ,KAAK,KAAK,MACjB,kBAAC,GAAD;WAAiB,SAAQ;WAAU,WAAU;qBAC1C;WACK,EAFI,EAEJ,CACR;UACE,CAAA;SAEJ;WACN,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACE,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAQ,WAAU,wBAAyB,CAAA,EAC3C,kBAAC,QAAD;YAAM,WAAU;sBAAqB,EAAQ,iBAAiB;YAAS,CAAA,CACnE;cACN,kBAAC,KAAD;WAAG,WAAU;qBAAoC;WAAY,CAAA,CACzD;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAU,WAAU,0BAA2B,CAAA,EAC/C,kBAAC,QAAD;YAAM,WAAU;sBAAqB,EAAQ;YAAiB,CAAA,CAC1D;cACN,kBAAC,KAAD;WAAG,WAAU;qBAAoC;WAAS,CAAA,CACtD;;SACN,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,OAAD;WAAK,WAAU;qBAAf,CACE,kBAAC,GAAD,EAAM,WAAU,2BAA4B,CAAA,EAC5C,kBAAC,QAAD;YAAM,WAAU;sBAAqB,EAAQ,iBAAiB;YAAS,CAAA,CACnE;cACN,kBAAC,KAAD;WAAG,WAAU;qBAAoC;WAAS,CAAA,CACtD;;SACF;UACF;;MACM,CAAA;KACT,EA7DA,EAAQ,GA6DR,CACP;IACE,CAAA;GACL,EAAO,WAAW,KACjB,kBAAC,KAAD;IAAG,WAAU;cAAyC;IAElD,CAAA;GAEF"}
|
|
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,
|
|
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: "
|
|
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: "
|
|
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} · 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} · 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":"
|
|
1
|
+
{"version":3,"file":"ProductsProductsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProductsProductsPagePage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,wBAAwB,4CA+KvC"}
|