@burdenoff/microfe-planmagnet 2026.927.3 → 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 +26 -0
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeatureControls.js +321 -0
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -0
- 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 +17 -0
- package/dist/planmagnet/controls/MilestoneControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/MilestoneControls.js +203 -0
- package/dist/planmagnet/controls/MilestoneControls.js.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts +31 -0
- package/dist/planmagnet/controls/ObjectiveControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ObjectiveControls.js +296 -0
- package/dist/planmagnet/controls/ObjectiveControls.js.map +1 -0
- package/dist/planmagnet/controls/Pickers.d.ts +26 -0
- package/dist/planmagnet/controls/Pickers.d.ts.map +1 -0
- package/dist/planmagnet/controls/Pickers.js +171 -0
- package/dist/planmagnet/controls/Pickers.js.map +1 -0
- 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 +14 -0
- package/dist/planmagnet/controls/ReleaseControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/ReleaseControls.js +111 -0
- package/dist/planmagnet/controls/ReleaseControls.js.map +1 -0
- 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 +190 -153
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.js +63 -56
- package/dist/planmagnet/pages/GoalsCreateObjectivePagePage.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 +229 -214
- 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 +145 -114
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.js +134 -105
- package/dist/planmagnet/pages/ProductsEditReleasePagePage.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 +169 -110
- 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/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +119 -94
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.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 +118 -107
- 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 +4 -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/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +55 -63
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- 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/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/TasksCreateTaskPagePage.js +123 -128
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +139 -107
- package/dist/planmagnet/pages/TasksEditTaskPagePage.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/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +173 -126
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +111 -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 +150 -137
- 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
|
@@ -2,13 +2,14 @@ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
|
2
2
|
import { PersonName as t } from "../people/People.js";
|
|
3
3
|
import { detailPath as n } from "../utils/rowNavigation.js";
|
|
4
4
|
import { useEntityId as r } from "../hooks/useEntityId.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
5
|
+
import { MilestoneHeaderActions as i } from "../controls/MilestoneControls.js";
|
|
6
|
+
import { Calendar as a, FolderKanban as o, Target as s, User as c } from "lucide-react";
|
|
7
|
+
import { gql as l } from "@apollo/client";
|
|
8
|
+
import { useQuery as u } from "@apollo/client/react";
|
|
9
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
10
|
+
import { Badge as p, Card as m, CardContent as h, CardHeader as g, CardTitle as _ } from "@burdenoff/fe-libs/ui";
|
|
10
11
|
//#region src/planmagnet/pages/ProjectsMilestoneDetailPagePage.tsx
|
|
11
|
-
var
|
|
12
|
+
var v = l`
|
|
12
13
|
query GetMilestonesForDetail($id: ID!) {
|
|
13
14
|
planmagnetMilestone(id: $id) {
|
|
14
15
|
id
|
|
@@ -26,154 +27,145 @@ var _ = c`
|
|
|
26
27
|
completedAt
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
|
|
30
|
-
PENDING: "bg-gray-100 text-gray-800",
|
|
31
|
-
IN_PROGRESS: "bg-blue-100 text-blue-800",
|
|
32
|
-
COMPLETED: "bg-green-100 text-green-800",
|
|
33
|
-
MISSED: "bg-red-100 text-red-800",
|
|
34
|
-
CANCELLED: "bg-red-100 text-red-800"
|
|
35
|
-
};
|
|
30
|
+
`;
|
|
36
31
|
function y() {
|
|
37
|
-
let
|
|
38
|
-
variables: { id:
|
|
39
|
-
skip: !
|
|
32
|
+
let l = r("milestoneId"), y = e(), { data: b, loading: x, error: S } = u(v, {
|
|
33
|
+
variables: { id: l },
|
|
34
|
+
skip: !l
|
|
40
35
|
});
|
|
41
|
-
if (!
|
|
36
|
+
if (!l) return /* @__PURE__ */ d("div", {
|
|
42
37
|
className: "p-6 text-muted-foreground",
|
|
43
38
|
children: "No milestone selected. Open one from the list."
|
|
44
39
|
});
|
|
45
|
-
if (x && !b) return /* @__PURE__ */
|
|
40
|
+
if (x && !b) return /* @__PURE__ */ d("div", {
|
|
46
41
|
className: "flex items-center justify-center p-12",
|
|
47
|
-
children: /* @__PURE__ */
|
|
42
|
+
children: /* @__PURE__ */ d("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
48
43
|
});
|
|
49
|
-
if (S) return /* @__PURE__ */
|
|
44
|
+
if (S) return /* @__PURE__ */ f("div", {
|
|
50
45
|
className: "p-6 text-red-500",
|
|
51
46
|
children: ["Error loading milestone: ", S.message]
|
|
52
47
|
});
|
|
53
48
|
let C = b?.planmagnetMilestone;
|
|
54
|
-
if (!C) return /* @__PURE__ */
|
|
49
|
+
if (!C) return /* @__PURE__ */ d("div", {
|
|
55
50
|
className: "p-6 text-muted-foreground",
|
|
56
51
|
children: "Milestone not found."
|
|
57
52
|
});
|
|
58
53
|
let w = C.status === "COMPLETED";
|
|
59
|
-
return /* @__PURE__ */
|
|
54
|
+
return /* @__PURE__ */ f("div", {
|
|
60
55
|
className: "space-y-6 p-6",
|
|
61
|
-
children: [/* @__PURE__ */
|
|
56
|
+
children: [/* @__PURE__ */ f("div", {
|
|
62
57
|
className: "flex items-center justify-between",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
58
|
+
children: [/* @__PURE__ */ f("div", {
|
|
64
59
|
className: "flex items-center gap-3",
|
|
65
|
-
children: [/* @__PURE__ */
|
|
60
|
+
children: [/* @__PURE__ */ d(s, { className: "h-6 w-6 text-primary" }), /* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("h1", {
|
|
66
61
|
className: "text-2xl font-semibold",
|
|
67
62
|
children: C.name
|
|
68
|
-
}), /* @__PURE__ */
|
|
63
|
+
}), /* @__PURE__ */ f("p", {
|
|
69
64
|
className: "text-sm text-muted-foreground",
|
|
70
65
|
children: ["Project: ", C.project?.name ?? "not available"]
|
|
71
66
|
})] })]
|
|
72
|
-
}), /* @__PURE__ */
|
|
73
|
-
|
|
74
|
-
children: C.status.toLowerCase().replace("_", " ")
|
|
75
|
-
})]
|
|
76
|
-
}), /* @__PURE__ */ d("div", {
|
|
67
|
+
}), /* @__PURE__ */ d(i, { milestone: C })]
|
|
68
|
+
}), /* @__PURE__ */ f("div", {
|
|
77
69
|
className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
70
|
+
children: [/* @__PURE__ */ f("div", {
|
|
79
71
|
className: "lg:col-span-2 space-y-6",
|
|
80
|
-
children: [/* @__PURE__ */
|
|
72
|
+
children: [/* @__PURE__ */ f(m, { children: [/* @__PURE__ */ d(g, { children: /* @__PURE__ */ d(_, { children: "Overview" }) }), /* @__PURE__ */ f(h, {
|
|
81
73
|
className: "space-y-4",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
74
|
+
children: [/* @__PURE__ */ d("p", {
|
|
83
75
|
className: "text-sm",
|
|
84
76
|
children: C.description ?? "No description provided."
|
|
85
|
-
}), /* @__PURE__ */
|
|
77
|
+
}), /* @__PURE__ */ d("div", { children: /* @__PURE__ */ f("div", {
|
|
86
78
|
className: "flex justify-between text-sm mb-2",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
79
|
+
children: [/* @__PURE__ */ d("span", {
|
|
88
80
|
className: "text-muted-foreground",
|
|
89
81
|
children: "Status"
|
|
90
|
-
}), /* @__PURE__ */
|
|
82
|
+
}), /* @__PURE__ */ d(p, {
|
|
91
83
|
variant: w ? "default" : "secondary",
|
|
92
84
|
className: "capitalize",
|
|
93
85
|
children: C.status.toLowerCase().replace("_", " ")
|
|
94
86
|
})]
|
|
95
87
|
}) })]
|
|
96
|
-
})] }), /* @__PURE__ */
|
|
88
|
+
})] }), /* @__PURE__ */ f(m, { children: [/* @__PURE__ */ d(g, { children: /* @__PURE__ */ d(_, { children: "Timeline" }) }), /* @__PURE__ */ d(h, { children: /* @__PURE__ */ f("div", {
|
|
97
89
|
className: "grid grid-cols-2 gap-4 text-center",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
90
|
+
children: [/* @__PURE__ */ f("div", {
|
|
99
91
|
className: "p-4 bg-muted rounded-md",
|
|
100
|
-
children: [/* @__PURE__ */
|
|
92
|
+
children: [/* @__PURE__ */ d("p", {
|
|
101
93
|
className: "text-sm font-bold",
|
|
102
94
|
children: C.dueDate ? new Date(C.dueDate).toLocaleDateString() : "—"
|
|
103
|
-
}), /* @__PURE__ */
|
|
95
|
+
}), /* @__PURE__ */ d("p", {
|
|
104
96
|
className: "text-xs text-muted-foreground",
|
|
105
97
|
children: "Due Date"
|
|
106
98
|
})]
|
|
107
|
-
}), /* @__PURE__ */
|
|
99
|
+
}), /* @__PURE__ */ f("div", {
|
|
108
100
|
className: "p-4 bg-muted rounded-md",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
101
|
+
children: [/* @__PURE__ */ d("p", {
|
|
110
102
|
className: "text-sm font-bold",
|
|
111
103
|
children: C.completedAt ? new Date(C.completedAt).toLocaleDateString() : "—"
|
|
112
|
-
}), /* @__PURE__ */
|
|
104
|
+
}), /* @__PURE__ */ d("p", {
|
|
113
105
|
className: "text-xs text-muted-foreground",
|
|
114
106
|
children: "Completed"
|
|
115
107
|
})]
|
|
116
108
|
})]
|
|
117
109
|
}) })] })]
|
|
118
|
-
}), /* @__PURE__ */
|
|
110
|
+
}), /* @__PURE__ */ f(m, { children: [/* @__PURE__ */ d(g, { children: /* @__PURE__ */ d(_, { children: "Details" }) }), /* @__PURE__ */ f(h, {
|
|
119
111
|
className: "space-y-3 text-sm",
|
|
120
112
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
113
|
+
/* @__PURE__ */ f("div", {
|
|
122
114
|
className: "flex items-center gap-2",
|
|
123
115
|
children: [
|
|
124
|
-
/* @__PURE__ */
|
|
125
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ d(a, { className: "h-4 w-4 text-muted-foreground" }),
|
|
117
|
+
/* @__PURE__ */ d("span", {
|
|
126
118
|
className: "text-muted-foreground",
|
|
127
119
|
children: "Due:"
|
|
128
120
|
}),
|
|
129
|
-
/* @__PURE__ */
|
|
121
|
+
/* @__PURE__ */ d("span", {
|
|
130
122
|
className: "font-medium",
|
|
131
123
|
children: C.dueDate ? new Date(C.dueDate).toLocaleDateString() : "Not set"
|
|
132
124
|
})
|
|
133
125
|
]
|
|
134
126
|
}),
|
|
135
|
-
/* @__PURE__ */
|
|
127
|
+
/* @__PURE__ */ f("div", {
|
|
136
128
|
className: "flex items-center gap-2",
|
|
137
129
|
children: [
|
|
138
|
-
/* @__PURE__ */
|
|
139
|
-
/* @__PURE__ */
|
|
130
|
+
/* @__PURE__ */ d(o, { className: "h-4 w-4 text-muted-foreground" }),
|
|
131
|
+
/* @__PURE__ */ d("span", {
|
|
140
132
|
className: "text-muted-foreground",
|
|
141
133
|
children: "Project:"
|
|
142
134
|
}),
|
|
143
|
-
C.project ? /* @__PURE__ */
|
|
135
|
+
C.project ? /* @__PURE__ */ d("button", {
|
|
144
136
|
type: "button",
|
|
145
137
|
className: "font-medium text-primary hover:underline",
|
|
146
138
|
onClick: () => y(n("/projects/project-detail-page", C.project.id)),
|
|
147
139
|
children: C.project.name
|
|
148
|
-
}) : /* @__PURE__ */
|
|
140
|
+
}) : /* @__PURE__ */ d("span", {
|
|
149
141
|
className: "text-muted-foreground",
|
|
150
142
|
children: "not available"
|
|
151
143
|
})
|
|
152
144
|
]
|
|
153
145
|
}),
|
|
154
|
-
C.ownerId && /* @__PURE__ */
|
|
146
|
+
C.ownerId && /* @__PURE__ */ f("div", {
|
|
155
147
|
className: "flex items-center gap-2",
|
|
156
148
|
children: [
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ d(c, { className: "h-4 w-4 text-muted-foreground" }),
|
|
150
|
+
/* @__PURE__ */ d("span", {
|
|
159
151
|
className: "text-muted-foreground",
|
|
160
152
|
children: "Owner:"
|
|
161
153
|
}),
|
|
162
|
-
/* @__PURE__ */
|
|
154
|
+
/* @__PURE__ */ d("span", {
|
|
163
155
|
className: "font-medium",
|
|
164
|
-
children: /* @__PURE__ */
|
|
156
|
+
children: /* @__PURE__ */ d(t, { id: C.ownerId })
|
|
165
157
|
})
|
|
166
158
|
]
|
|
167
159
|
}),
|
|
168
|
-
/* @__PURE__ */
|
|
160
|
+
/* @__PURE__ */ f("div", {
|
|
169
161
|
className: "flex items-center gap-2",
|
|
170
162
|
children: [
|
|
171
|
-
/* @__PURE__ */
|
|
172
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ d(a, { className: "h-4 w-4 text-muted-foreground" }),
|
|
164
|
+
/* @__PURE__ */ d("span", {
|
|
173
165
|
className: "text-muted-foreground",
|
|
174
166
|
children: "Created:"
|
|
175
167
|
}),
|
|
176
|
-
/* @__PURE__ */
|
|
168
|
+
/* @__PURE__ */ d("span", {
|
|
177
169
|
className: "font-medium",
|
|
178
170
|
children: C.createdAt ? new Date(C.createdAt).toLocaleDateString() : "—"
|
|
179
171
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsMilestoneDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsMilestoneDetailPagePage.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 { Target, Calendar, FolderKanban, User } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { PersonName } from '../people/People';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\n\nconst GET_MILESTONES_FOR_DETAIL = gql`\n query GetMilestonesForDetail($id: ID!) {\n planmagnetMilestone(id: $id) {\n id\n name\n description\n projectId\n project {\n id\n name\n }\n status\n dueDate\n ownerId\n createdAt\n completedAt\n }\n }\n`;\n\nconst statusColor: Record<string, string> = {\n PENDING: 'bg-gray-100 text-gray-800',\n IN_PROGRESS: 'bg-blue-100 text-blue-800',\n COMPLETED: 'bg-green-100 text-green-800',\n MISSED: 'bg-red-100 text-red-800',\n CANCELLED: 'bg-red-100 text-red-800',\n};\n\nexport function ProjectsMilestoneDetailPagePage() {\n const milestoneId = useEntityId('milestoneId');\n const navigate = usePlanMagnetNavigate();\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_MILESTONES_FOR_DETAIL, {\n variables: { id: milestoneId },\n skip: !milestoneId,\n });\n\n if (!milestoneId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No milestone selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch must not blank the page (it\n // unmounts every component on it and loses their state).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading milestone: {error.message}</div>;\n\n const ms = data?.planmagnetMilestone;\n if (!ms) return <div className=\"p-6 text-muted-foreground\">Milestone not found.</div>;\n\n const isCompleted = ms.status === 'COMPLETED';\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 <Target className=\"h-6 w-6 text-primary\" />\n <div>\n <h1 className=\"text-2xl font-semibold\">{ms.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Project: {ms.project?.name ?? 'not available'}\n </p>\n </div>\n </div>\n <span\n className={`text-xs px-3 py-1 rounded-full capitalize ${statusColor[ms.status] ?? 'bg-gray-100'}`}\n >\n {ms.status.toLowerCase().replace('_', ' ')}\n </span>\n </div>\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>Overview</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{ms.description ?? 'No description provided.'}</p>\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant={isCompleted ? 'default' : 'secondary'} className=\"capitalize\">\n {ms.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Timeline</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-sm font-bold\">\n {ms.dueDate ? new Date(ms.dueDate).toLocaleDateString() : '—'}\n </p>\n <p className=\"text-xs text-muted-foreground\">Due Date</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-sm font-bold\">\n {ms.completedAt ? new Date(ms.completedAt).toLocaleDateString() : '—'}\n </p>\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Due:</span>\n <span className=\"font-medium\">\n {ms.dueDate ? new Date(ms.dueDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n {/* The project's name, linked — this printed the raw project UUID (BOFF-7949). */}\n {ms.project ? (\n <button\n type=\"button\"\n className=\"font-medium text-primary hover:underline\"\n onClick={() =>\n navigate(detailPath('/projects/project-detail-page', ms.project.id))\n }\n >\n {ms.project.name}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">not available</span>\n )}\n </div>\n {ms.ownerId && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Owner:</span>\n <span className=\"font-medium\">\n <PersonName id={ms.ownerId} />\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"font-medium\">\n {ms.createdAt ? new Date(ms.createdAt).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectsMilestoneDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsMilestoneDetailPagePage.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 { Target, Calendar, FolderKanban, User } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { PersonName } from '../people/People';\nimport { MilestoneHeaderActions } from '../controls/MilestoneControls';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\n\nconst GET_MILESTONES_FOR_DETAIL = gql`\n query GetMilestonesForDetail($id: ID!) {\n planmagnetMilestone(id: $id) {\n id\n name\n description\n projectId\n project {\n id\n name\n }\n status\n dueDate\n ownerId\n createdAt\n completedAt\n }\n }\n`;\n\nexport function ProjectsMilestoneDetailPagePage() {\n const milestoneId = useEntityId('milestoneId');\n const navigate = usePlanMagnetNavigate();\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_MILESTONES_FOR_DETAIL, {\n variables: { id: milestoneId },\n skip: !milestoneId,\n });\n\n if (!milestoneId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No milestone selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch must not blank the page (it\n // unmounts every component on it and loses their state).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading milestone: {error.message}</div>;\n\n const ms = data?.planmagnetMilestone;\n if (!ms) return <div className=\"p-6 text-muted-foreground\">Milestone not found.</div>;\n\n const isCompleted = ms.status === 'COMPLETED';\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 <Target className=\"h-6 w-6 text-primary\" />\n <div>\n <h1 className=\"text-2xl font-semibold\">{ms.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n Project: {ms.project?.name ?? 'not available'}\n </p>\n </div>\n </div>\n {/* Status, Edit and Delete (BOFF-7953): the milestone could never move past Pending. */}\n <MilestoneHeaderActions milestone={ms} />\n </div>\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>Overview</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm\">{ms.description ?? 'No description provided.'}</p>\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Status</span>\n <Badge variant={isCompleted ? 'default' : 'secondary'} className=\"capitalize\">\n {ms.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Timeline</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-sm font-bold\">\n {ms.dueDate ? new Date(ms.dueDate).toLocaleDateString() : '—'}\n </p>\n <p className=\"text-xs text-muted-foreground\">Due Date</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-sm font-bold\">\n {ms.completedAt ? new Date(ms.completedAt).toLocaleDateString() : '—'}\n </p>\n <p className=\"text-xs text-muted-foreground\">Completed</p>\n </div>\n </div>\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Due:</span>\n <span className=\"font-medium\">\n {ms.dueDate ? new Date(ms.dueDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n {/* The project's name, linked — this printed the raw project UUID (BOFF-7949). */}\n {ms.project ? (\n <button\n type=\"button\"\n className=\"font-medium text-primary hover:underline\"\n onClick={() =>\n navigate(detailPath('/projects/project-detail-page', ms.project.id))\n }\n >\n {ms.project.name}\n </button>\n ) : (\n <span className=\"text-muted-foreground\">not available</span>\n )}\n </div>\n {ms.ownerId && (\n <div className=\"flex items-center gap-2\">\n <User className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Owner:</span>\n <span className=\"font-medium\">\n <PersonName id={ms.ownerId} />\n </span>\n </div>\n )}\n <div className=\"flex items-center gap-2\">\n <Calendar className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Created:</span>\n <span className=\"font-medium\">\n {ms.createdAt ? new Date(ms.createdAt).toLocaleDateString() : '—'}\n </span>\n </div>\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPlanningDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.tsx"],"names":[],"mappings":"AA8EA,wBAAgB,8BAA8B,
|
|
1
|
+
{"version":3,"file":"ProjectsPlanningDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.tsx"],"names":[],"mappings":"AA8EA,wBAAgB,8BAA8B,4CA2R7C"}
|
|
@@ -103,7 +103,7 @@ function T() {
|
|
|
103
103
|
};
|
|
104
104
|
let N = A.workItems ?? [], P = N.reduce((e, t) => e + (t.storyPoints ?? 0), 0);
|
|
105
105
|
N.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0);
|
|
106
|
-
let F = A.capacity ??
|
|
106
|
+
let F = A.capacity ?? null, I = F && F > 0 ? Math.round(P / F * 100) : null, L = F == null ? null : Math.max(0, F - P), R = [...new Set(N.map((e) => e.assigneeId).filter(Boolean))];
|
|
107
107
|
return /* @__PURE__ */ g("div", {
|
|
108
108
|
className: "space-y-6 p-6",
|
|
109
109
|
children: [/* @__PURE__ */ g("div", {
|
|
@@ -156,10 +156,10 @@ function T() {
|
|
|
156
156
|
className: "p-4 bg-muted rounded-md",
|
|
157
157
|
children: [/* @__PURE__ */ h("p", {
|
|
158
158
|
className: "text-2xl font-bold",
|
|
159
|
-
children: F
|
|
159
|
+
children: F ?? "—"
|
|
160
160
|
}), /* @__PURE__ */ h("p", {
|
|
161
161
|
className: "text-xs text-muted-foreground",
|
|
162
|
-
children: "Capacity"
|
|
162
|
+
children: F == null ? "No capacity set" : "Capacity"
|
|
163
163
|
})]
|
|
164
164
|
}),
|
|
165
165
|
/* @__PURE__ */ g("div", {
|
|
@@ -176,14 +176,17 @@ function T() {
|
|
|
176
176
|
className: "p-4 bg-muted rounded-md",
|
|
177
177
|
children: [/* @__PURE__ */ h("p", {
|
|
178
178
|
className: "text-2xl font-bold",
|
|
179
|
-
children: L
|
|
179
|
+
children: L ?? "—"
|
|
180
180
|
}), /* @__PURE__ */ h("p", {
|
|
181
181
|
className: "text-xs text-muted-foreground",
|
|
182
182
|
children: "Remaining"
|
|
183
183
|
})]
|
|
184
184
|
})
|
|
185
185
|
]
|
|
186
|
-
}),
|
|
186
|
+
}), I === null ? /* @__PURE__ */ h("p", {
|
|
187
|
+
className: "text-sm text-muted-foreground",
|
|
188
|
+
children: "Set a capacity on the sprint to see its utilisation."
|
|
189
|
+
}) : /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ g("div", {
|
|
187
190
|
className: "flex justify-between text-sm mb-2",
|
|
188
191
|
children: [/* @__PURE__ */ h("span", {
|
|
189
192
|
className: "text-muted-foreground",
|
|
@@ -195,7 +198,7 @@ function T() {
|
|
|
195
198
|
}), /* @__PURE__ */ h("div", {
|
|
196
199
|
className: "w-full h-4 rounded-full bg-muted overflow-hidden",
|
|
197
200
|
children: /* @__PURE__ */ h("div", {
|
|
198
|
-
className: `h-full rounded-full ${I > 90 ? "bg-
|
|
201
|
+
className: `h-full rounded-full ${I > 90 ? "bg-destructive" : "bg-success"}`,
|
|
199
202
|
style: { width: `${Math.min(I, 100)}%` }
|
|
200
203
|
})
|
|
201
204
|
})] })]
|
|
@@ -295,9 +298,9 @@ function T() {
|
|
|
295
298
|
className: "text-muted-foreground",
|
|
296
299
|
children: "Capacity:"
|
|
297
300
|
}),
|
|
298
|
-
/* @__PURE__ */
|
|
301
|
+
/* @__PURE__ */ h("span", {
|
|
299
302
|
className: "font-medium",
|
|
300
|
-
children:
|
|
303
|
+
children: F == null ? "not set" : `${F} pts`
|
|
301
304
|
})
|
|
302
305
|
]
|
|
303
306
|
}),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPlanningDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.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, Button } from '@burdenoff/fe-libs/ui';\nimport { Users, BarChart3, FolderKanban, Pencil } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonAvatar, PersonName, initialsOf } from '../people/People';\nimport { SprintLifecycleActions, SprintWorkItemsPanel } from '../sprints/SprintControls';\nimport { SprintDates } from '../sprints/SprintDates';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n project {\n id\n name\n }\n status\n startDate\n endDate\n startedAt\n completedAt\n baselineStartDate\n baselineEndDate\n planChanges {\n field\n fromValue\n toValue\n sprintStatus\n changedBy\n changedAt\n }\n goal\n capacity\n velocity\n workItems {\n id\n key\n title\n status\n storyPoints\n assigneeId\n priority\n }\n }\n }\n`;\n\n// Real cross-layer CrewFoundry workforce capacity (BOFF-4444). Fails open on\n// the backend — an unreachable/unprovisioned CrewFoundry never blocks this\n// page, it just yields crewfoundryUnavailable: true, handled below.\nconst GET_SPRINT_CAPACITY = gql`\n query GetSprintCapacity($sprintId: ID!) {\n planmagnetSprintCapacity(sprintId: $sprintId) {\n sprintId\n crewfoundryUnavailable\n unmatchedAssigneeIds\n members {\n actorId\n employeeName\n department\n position\n allocatedHours\n billableHours\n utilizationPercent\n assignedStoryPoints\n assignedWorkItemCount\n }\n }\n }\n`;\n\nexport function ProjectsPlanningDetailPagePage() {\n const sprintId = useEntityId('sprintId');\n const navigate = usePlanMagnetNavigate();\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_SPRINT_DETAIL, {\n variables: { id: sprintId },\n skip: !sprintId,\n });\n\n // Real cross-layer CrewFoundry capacity (BOFF-4444) — called unconditionally\n // (with `skip`) to keep hook order stable across the loading/error/loaded\n // renders above.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: capacityData } = useQuery<Record<string, any>>(GET_SPRINT_CAPACITY, {\n variables: { sprintId },\n skip: !sprintId,\n });\n\n if (!sprintId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No planning session selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading planning session: {error.message}</div>;\n\n const sprint = data?.planmagnetSprint;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n const capacity = capacityData?.planmagnetSprintCapacity;\n const allocationByAssignee: Record<\n string,\n {\n employeeName: string;\n allocatedHours: number;\n billableHours: number;\n utilizationPercent: number | null;\n }\n > = {};\n for (const member of capacity?.members ?? []) {\n allocationByAssignee[member.actorId] = {\n employeeName: member.employeeName,\n allocatedHours: member.allocatedHours,\n billableHours: member.billableHours,\n utilizationPercent: member.utilizationPercent,\n };\n }\n\n const workItems = sprint.workItems ?? [];\n const totalPoints = workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const completedPoints = workItems\n .filter((wi: { status: string }) => wi.status === 'DONE')\n .reduce((sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0), 0);\n const allocatedCapacity = sprint.capacity ?? totalPoints;\n const capPct = allocatedCapacity > 0 ? Math.round((totalPoints / allocatedCapacity) * 100) : 0;\n const remaining = allocatedCapacity - totalPoints;\n\n // Derive unique assignees from work items\n const uniqueAssignees = [\n ...new Set(\n workItems\n .map((wi: { assigneeId: string | null }) => wi.assigneeId)\n .filter(Boolean) as string[]\n ),\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\">{sprint.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprint.project?.name ?? 'Unknown project'}\n </p>\n <div className=\"mt-1\">\n <SprintDates sprint={sprint} />\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n {/* The edit page existed but nothing linked to it, so a sprint's\n name, planned dates, goal and capacity could not be changed from\n the UI (BOFF-7954). Finished sprints are not edited here. */}\n {sprint.status !== 'COMPLETED' && sprint.status !== 'CANCELLED' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/projects/planning-edit-page', sprint.id))}\n data-testid=\"sprint-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\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>Capacity Allocation</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{allocatedCapacity}</p>\n <p className=\"text-xs text-muted-foreground\">Capacity</p>\n </div>\n <div className=\"p-4 bg-primary/10 rounded-md\">\n <p className=\"text-2xl font-bold text-primary\">{totalPoints}</p>\n <p className=\"text-xs text-muted-foreground\">Allocated</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{remaining > 0 ? remaining : 0}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Utilization</span>\n <span className=\"font-medium\">{capPct}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-red-400' : 'bg-green-400'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Work Items</CardTitle>\n </CardHeader>\n <CardContent>\n <SprintWorkItemsPanel\n sprintId={sprint.id}\n projectId={sprint.projectId}\n status={sprint.status}\n items={workItems}\n />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Team Members ({uniqueAssignees.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-3\">\n {uniqueAssignees.map((assigneeId: string) => {\n const workforce = allocationByAssignee[assigneeId];\n return (\n <div\n key={assigneeId}\n className=\"flex items-center gap-2 border rounded-md p-2.5\"\n >\n {/* Initials from the name shown next to them, not from the UUID (BOFF-8000). */}\n {workforce?.employeeName ? (\n <span\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n title={workforce.employeeName}\n >\n {initialsOf(workforce.employeeName) || '?'}\n </span>\n ) : (\n <PersonAvatar\n id={assigneeId}\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n />\n )}\n <div className=\"min-w-0\">\n <span className=\"text-sm truncate block\">\n {workforce?.employeeName ?? <PersonName id={assigneeId} />}\n </span>\n {workforce && (\n <span className=\"text-xs text-muted-foreground\">\n {workforce.allocatedHours}h allocated ({workforce.billableHours}h\n billable)\n {workforce.utilizationPercent != null &&\n ` · ${workforce.utilizationPercent}% util`}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {uniqueAssignees.length === 0 && (\n <p className=\"text-sm text-muted-foreground col-span-2 text-center py-4\">\n No team members assigned.\n </p>\n )}\n </div>\n {/* Real cross-layer CrewFoundry workforce allocation (BOFF-4444). */}\n {capacity?.crewfoundryUnavailable && uniqueAssignees.length > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n Workforce allocation from CrewFoundry is unavailable for this workspace — no\n CrewFoundry module provisioned yet.\n </p>\n )}\n {!capacity?.crewfoundryUnavailable &&\n (capacity?.unmatchedAssigneeIds?.length ?? 0) > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n {capacity.unmatchedAssigneeIds.length} assignee(s) have no matching CrewFoundry\n employee record — allocation hours shown only for matched employees.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Session Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n <span className=\"font-medium\">{sprint.project?.name ?? 'Unknown project'}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Assignees:</span>\n <span className=\"font-medium\">{uniqueAssignees.length}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Capacity:</span>\n <span className=\"font-medium\">{allocatedCapacity} pts</span>\n </div>\n {sprint.velocity != null && (\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Velocity:</span>\n <span className=\"font-medium\">{sprint.velocity} pts</span>\n </div>\n )}\n {sprint.goal && (\n <div className=\"border-t pt-3 mt-3\">\n <p className=\"text-muted-foreground text-xs mb-1\">Goal</p>\n <p className=\"text-sm\">{sprint.goal}</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectsPlanningDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.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, Button } from '@burdenoff/fe-libs/ui';\nimport { Users, BarChart3, FolderKanban, Pencil } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonAvatar, PersonName, initialsOf } from '../people/People';\nimport { SprintLifecycleActions, SprintWorkItemsPanel } from '../sprints/SprintControls';\nimport { SprintDates } from '../sprints/SprintDates';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n project {\n id\n name\n }\n status\n startDate\n endDate\n startedAt\n completedAt\n baselineStartDate\n baselineEndDate\n planChanges {\n field\n fromValue\n toValue\n sprintStatus\n changedBy\n changedAt\n }\n goal\n capacity\n velocity\n workItems {\n id\n key\n title\n status\n storyPoints\n assigneeId\n priority\n }\n }\n }\n`;\n\n// Real cross-layer CrewFoundry workforce capacity (BOFF-4444). Fails open on\n// the backend — an unreachable/unprovisioned CrewFoundry never blocks this\n// page, it just yields crewfoundryUnavailable: true, handled below.\nconst GET_SPRINT_CAPACITY = gql`\n query GetSprintCapacity($sprintId: ID!) {\n planmagnetSprintCapacity(sprintId: $sprintId) {\n sprintId\n crewfoundryUnavailable\n unmatchedAssigneeIds\n members {\n actorId\n employeeName\n department\n position\n allocatedHours\n billableHours\n utilizationPercent\n assignedStoryPoints\n assignedWorkItemCount\n }\n }\n }\n`;\n\nexport function ProjectsPlanningDetailPagePage() {\n const sprintId = useEntityId('sprintId');\n const navigate = usePlanMagnetNavigate();\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_SPRINT_DETAIL, {\n variables: { id: sprintId },\n skip: !sprintId,\n });\n\n // Real cross-layer CrewFoundry capacity (BOFF-4444) — called unconditionally\n // (with `skip`) to keep hook order stable across the loading/error/loaded\n // renders above.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: capacityData } = useQuery<Record<string, any>>(GET_SPRINT_CAPACITY, {\n variables: { sprintId },\n skip: !sprintId,\n });\n\n if (!sprintId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No planning session selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading planning session: {error.message}</div>;\n\n const sprint = data?.planmagnetSprint;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n const capacity = capacityData?.planmagnetSprintCapacity;\n const allocationByAssignee: Record<\n string,\n {\n employeeName: string;\n allocatedHours: number;\n billableHours: number;\n utilizationPercent: number | null;\n }\n > = {};\n for (const member of capacity?.members ?? []) {\n allocationByAssignee[member.actorId] = {\n employeeName: member.employeeName,\n allocatedHours: member.allocatedHours,\n billableHours: member.billableHours,\n utilizationPercent: member.utilizationPercent,\n };\n }\n\n const workItems = sprint.workItems ?? [];\n const totalPoints = workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const completedPoints = workItems\n .filter((wi: { status: string }) => wi.status === 'DONE')\n .reduce((sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0), 0);\n // No capacity set means \"not planned\", not \"full\": this used to take the allocated points as the capacity and show\n // 100% utilisation in red, while the list showed no capacity for the same sprint (BOFF-7974).\n const sprintCapacity: number | null = sprint.capacity ?? null;\n const capPct =\n sprintCapacity && sprintCapacity > 0 ? Math.round((totalPoints / sprintCapacity) * 100) : null;\n const remaining = sprintCapacity == null ? null : Math.max(0, sprintCapacity - totalPoints);\n\n // Derive unique assignees from work items\n const uniqueAssignees = [\n ...new Set(\n workItems\n .map((wi: { assigneeId: string | null }) => wi.assigneeId)\n .filter(Boolean) as string[]\n ),\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\">{sprint.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprint.project?.name ?? 'Unknown project'}\n </p>\n <div className=\"mt-1\">\n <SprintDates sprint={sprint} />\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n {/* The edit page existed but nothing linked to it, so a sprint's\n name, planned dates, goal and capacity could not be changed from\n the UI (BOFF-7954). Finished sprints are not edited here. */}\n {sprint.status !== 'COMPLETED' && sprint.status !== 'CANCELLED' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/projects/planning-edit-page', sprint.id))}\n data-testid=\"sprint-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\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>Capacity Allocation</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{sprintCapacity ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">\n {sprintCapacity == null ? 'No capacity set' : 'Capacity'}\n </p>\n </div>\n <div className=\"p-4 bg-primary/10 rounded-md\">\n <p className=\"text-2xl font-bold text-primary\">{totalPoints}</p>\n <p className=\"text-xs text-muted-foreground\">Allocated</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{remaining ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n {capPct !== null ? (\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Utilization</span>\n <span className=\"font-medium\">{capPct}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-destructive' : 'bg-success'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Set a capacity on the sprint to see its utilisation.\n </p>\n )}\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Work Items</CardTitle>\n </CardHeader>\n <CardContent>\n <SprintWorkItemsPanel\n sprintId={sprint.id}\n projectId={sprint.projectId}\n status={sprint.status}\n items={workItems}\n />\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Team Members ({uniqueAssignees.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-3\">\n {uniqueAssignees.map((assigneeId: string) => {\n const workforce = allocationByAssignee[assigneeId];\n return (\n <div\n key={assigneeId}\n className=\"flex items-center gap-2 border rounded-md p-2.5\"\n >\n {/* Initials from the name shown next to them, not from the UUID (BOFF-8000). */}\n {workforce?.employeeName ? (\n <span\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n title={workforce.employeeName}\n >\n {initialsOf(workforce.employeeName) || '?'}\n </span>\n ) : (\n <PersonAvatar\n id={assigneeId}\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n />\n )}\n <div className=\"min-w-0\">\n <span className=\"text-sm truncate block\">\n {workforce?.employeeName ?? <PersonName id={assigneeId} />}\n </span>\n {workforce && (\n <span className=\"text-xs text-muted-foreground\">\n {workforce.allocatedHours}h allocated ({workforce.billableHours}h\n billable)\n {workforce.utilizationPercent != null &&\n ` · ${workforce.utilizationPercent}% util`}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {uniqueAssignees.length === 0 && (\n <p className=\"text-sm text-muted-foreground col-span-2 text-center py-4\">\n No team members assigned.\n </p>\n )}\n </div>\n {/* Real cross-layer CrewFoundry workforce allocation (BOFF-4444). */}\n {capacity?.crewfoundryUnavailable && uniqueAssignees.length > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n Workforce allocation from CrewFoundry is unavailable for this workspace — no\n CrewFoundry module provisioned yet.\n </p>\n )}\n {!capacity?.crewfoundryUnavailable &&\n (capacity?.unmatchedAssigneeIds?.length ?? 0) > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n {capacity.unmatchedAssigneeIds.length} assignee(s) have no matching CrewFoundry\n employee record — allocation hours shown only for matched employees.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Session Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n <span className=\"font-medium\">{sprint.project?.name ?? 'Unknown project'}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Assignees:</span>\n <span className=\"font-medium\">{uniqueAssignees.length}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Capacity:</span>\n <span className=\"font-medium\">\n {sprintCapacity == null ? 'not set' : `${sprintCapacity} pts`}\n </span>\n </div>\n {sprint.velocity != null && (\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Velocity:</span>\n <span className=\"font-medium\">{sprint.velocity} pts</span>\n </div>\n )}\n {sprint.goal && (\n <div className=\"border-t pt-3 mt-3\">\n <p className=\"text-muted-foreground text-xs mb-1\">Goal</p>\n <p className=\"text-sm\">{sprint.goal}</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPlanningPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,wBAAwB,
|
|
1
|
+
{"version":3,"file":"ProjectsPlanningPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,wBAAwB,4CAuIvC"}
|
|
@@ -61,7 +61,7 @@ function v() {
|
|
|
61
61
|
children: "Planning Sessions"
|
|
62
62
|
}), /* @__PURE__ */ u("p", {
|
|
63
63
|
className: "text-sm text-muted-foreground",
|
|
64
|
-
children: [S.length, " sprints across all projects"]
|
|
64
|
+
children: [y?.planmagnetSprints?.totalCount ?? S.length, " sprints across all projects"]
|
|
65
65
|
})] }), /* @__PURE__ */ l(i, { onCreated: (e) => v(n("/projects/planning-detail-page", e)) })]
|
|
66
66
|
}),
|
|
67
67
|
/* @__PURE__ */ l("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPlanningPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.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 { CalendarClock, FolderKanban } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { NewSprintDialog } from '../sprints/SprintControls';\n\nconst GET_SPRINTS = gql`\n query GetSprints($workspaceId: ID!) {\n planmagnetSprints(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n projectId\n project {\n id\n name\n }\n status\n startDate\n endDate\n goal\n capacity\n velocity\n workItems {\n id\n status\n storyPoints\n }\n }\n totalCount\n }\n }\n`;\n\nconst statusColor: Record<string, string> = {\n PLANNING: 'bg-blue-100 text-blue-800',\n ACTIVE: 'bg-green-100 text-green-800',\n REVIEW: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-gray-100 text-gray-800',\n CANCELLED: 'bg-red-100 text-red-800',\n};\n\nexport function ProjectsPlanningPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\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_SPRINTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n });\n\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading planning sessions: {error.message}</div>;\n\n const sprints = data?.planmagnetSprints?.items ?? [];\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\">Planning Sessions</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprints.length} sprints across all projects\n </p>\n </div>\n <NewSprintDialog\n onCreated={(id) => navigate(detailPath('/projects/planning-detail-page', id))}\n />\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {sprints.map(\n (sprint: {\n id: string;\n name: string;\n projectId: string;\n project: { id: string; name: string } | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n goal: string | null;\n capacity: number | null;\n velocity: number | null;\n workItems: { id: string; status: string; storyPoints: number | null }[];\n }) => {\n const totalPoints = sprint.workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const allocatedCapacity = sprint.capacity ?? 0;\n const capPct =\n allocatedCapacity > 0 ? Math.round((totalPoints / allocatedCapacity) * 100) : 0;\n return (\n <Card\n key={sprint.id}\n {...rowLinkProps(\n () => navigate(detailPath('/projects/planning-detail-page', sprint.id)),\n sprint.name\n )}\n >\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-primary\" />\n {sprint.name}\n </CardTitle>\n <Badge\n variant={sprint.status === 'COMPLETED' ? 'default' : 'secondary'}\n className=\"capitalize\"\n >\n {sprint.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[sprint.status] ?? 'bg-gray-100'}`}\n >\n {sprint.status.toLowerCase().replace('_', ' ')}\n </span>\n <span className=\"text-xs text-muted-foreground flex items-center gap-1\">\n <FolderKanban className=\"h-3 w-3\" />\n {sprint.project?.name ?? 'Unknown project'}\n </span>\n </div>\n {sprint.goal && <p className=\"text-sm text-muted-foreground\">{sprint.goal}</p>}\n <div className=\"flex gap-3 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <CalendarClock className=\"h-3 w-3\" />\n {sprint.startDate\n ? new Date(sprint.startDate).toLocaleDateString()\n : '—'} -{' '}\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : '—'}\n </span>\n <span>{sprint.workItems.length} work items</span>\n </div>\n {allocatedCapacity > 0 && (\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Capacity</span>\n <span className=\"font-medium\">\n {totalPoints}/{allocatedCapacity} pts ({capPct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-red-400' : 'bg-primary'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n );\n }\n )}\n </div>\n {sprints.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">No planning sessions found.</p>\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;AAUA,IAAM,IAAc,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BjB,IAAsC;CAC1C,UAAU;CACV,QAAQ;CACR,QAAQ;CACR,WAAW;CACX,WAAW;CACZ;AAED,SAAgB,IAA2B;CACzC,IAAM,EAAE,mBAAgB,GAAe,EACjC,IAAW,GAAuB,EAElC,EACJ,SACA,YACA,aAGA,EAA8B,GAAa,EACzC,WAAW,EAAE,aAAa,KAAe,WAAW,EACrD,CAAC;AAIJ,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;EAC9F,CAAA;AAEV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAkC,qCAAkC,EAAM,QAAc;;CAEjG,IAAM,IAAU,GAAM,mBAAmB,SAAS,EAAE;AAEpD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAyB;KAAsB,CAAA,EAC7D,kBAAC,KAAD;KAAG,WAAU;eAAb,
|
|
1
|
+
{"version":3,"file":"ProjectsPlanningPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.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 { CalendarClock, FolderKanban } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { rowLinkProps, detailPath } from '../utils/rowNavigation';\nimport { NewSprintDialog } from '../sprints/SprintControls';\n\nconst GET_SPRINTS = gql`\n query GetSprints($workspaceId: ID!) {\n planmagnetSprints(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n projectId\n project {\n id\n name\n }\n status\n startDate\n endDate\n goal\n capacity\n velocity\n workItems {\n id\n status\n storyPoints\n }\n }\n totalCount\n }\n }\n`;\n\nconst statusColor: Record<string, string> = {\n PLANNING: 'bg-blue-100 text-blue-800',\n ACTIVE: 'bg-green-100 text-green-800',\n REVIEW: 'bg-purple-100 text-purple-800',\n COMPLETED: 'bg-gray-100 text-gray-800',\n CANCELLED: 'bg-red-100 text-red-800',\n};\n\nexport function ProjectsPlanningPagePage() {\n const { workspaceId } = usePlanMagnet();\n const navigate = usePlanMagnetNavigate();\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_SPRINTS, {\n variables: { workspaceId: workspaceId ?? 'default' },\n });\n\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-red-500\">Error loading planning sessions: {error.message}</div>;\n\n const sprints = data?.planmagnetSprints?.items ?? [];\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\">Planning Sessions</h1>\n <p className=\"text-sm text-muted-foreground\">\n {/* The server total, not the loaded page's length (BOFF-7970). */}\n {data?.planmagnetSprints?.totalCount ?? sprints.length} sprints across all projects\n </p>\n </div>\n <NewSprintDialog\n onCreated={(id) => navigate(detailPath('/projects/planning-detail-page', id))}\n />\n </div>\n <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n {sprints.map(\n (sprint: {\n id: string;\n name: string;\n projectId: string;\n project: { id: string; name: string } | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n goal: string | null;\n capacity: number | null;\n velocity: number | null;\n workItems: { id: string; status: string; storyPoints: number | null }[];\n }) => {\n const totalPoints = sprint.workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const allocatedCapacity = sprint.capacity ?? 0;\n const capPct =\n allocatedCapacity > 0 ? Math.round((totalPoints / allocatedCapacity) * 100) : 0;\n return (\n <Card\n key={sprint.id}\n {...rowLinkProps(\n () => navigate(detailPath('/projects/planning-detail-page', sprint.id)),\n sprint.name\n )}\n >\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-primary\" />\n {sprint.name}\n </CardTitle>\n <Badge\n variant={sprint.status === 'COMPLETED' ? 'default' : 'secondary'}\n className=\"capitalize\"\n >\n {sprint.status.toLowerCase().replace('_', ' ')}\n </Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"flex gap-2\">\n <span\n className={`text-xs px-2 py-0.5 rounded-full capitalize ${statusColor[sprint.status] ?? 'bg-gray-100'}`}\n >\n {sprint.status.toLowerCase().replace('_', ' ')}\n </span>\n <span className=\"text-xs text-muted-foreground flex items-center gap-1\">\n <FolderKanban className=\"h-3 w-3\" />\n {sprint.project?.name ?? 'Unknown project'}\n </span>\n </div>\n {sprint.goal && <p className=\"text-sm text-muted-foreground\">{sprint.goal}</p>}\n <div className=\"flex gap-3 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <CalendarClock className=\"h-3 w-3\" />\n {sprint.startDate\n ? new Date(sprint.startDate).toLocaleDateString()\n : '—'} -{' '}\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : '—'}\n </span>\n <span>{sprint.workItems.length} work items</span>\n </div>\n {allocatedCapacity > 0 && (\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Capacity</span>\n <span className=\"font-medium\">\n {totalPoints}/{allocatedCapacity} pts ({capPct}%)\n </span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-red-400' : 'bg-primary'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n )}\n </CardContent>\n </Card>\n );\n }\n )}\n </div>\n {sprints.length === 0 && (\n <p className=\"text-center text-muted-foreground py-8\">No planning sessions found.</p>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"AA2EA,wBAAgB,6BAA6B,4CAgV5C"}
|