@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,26 +1,26 @@
|
|
|
1
1
|
import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import { ConfirmButton as
|
|
4
|
-
import { enumLabel as
|
|
5
|
-
import { useState as
|
|
6
|
-
import { Pencil as
|
|
7
|
-
import { gql as
|
|
8
|
-
import { useMutation as
|
|
9
|
-
import { jsx as
|
|
10
|
-
import { Button as
|
|
2
|
+
import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
|
|
3
|
+
import { ConfirmButton as r, ErrorLine as i } from "./ConfirmButton.js";
|
|
4
|
+
import { enumLabel as a } from "./WorkItemControls.js";
|
|
5
|
+
import { useState as o } from "react";
|
|
6
|
+
import { Pencil as s, Trash2 as c } from "lucide-react";
|
|
7
|
+
import { gql as l } from "@apollo/client";
|
|
8
|
+
import { useMutation as u } from "@apollo/client/react";
|
|
9
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
10
|
+
import { Button as p, Input as m, ResponsiveDialog as h, ResponsiveDialogContent as g, ResponsiveDialogDescription as _, ResponsiveDialogFooter as v, ResponsiveDialogHeader as y, ResponsiveDialogTitle as b, ResponsiveDialogTrigger as x } from "@burdenoff/fe-libs/ui";
|
|
11
11
|
//#region src/planmagnet/controls/MilestoneControls.tsx
|
|
12
|
-
var
|
|
12
|
+
var S = [
|
|
13
13
|
"PENDING",
|
|
14
14
|
"IN_PROGRESS",
|
|
15
15
|
"COMPLETED",
|
|
16
16
|
"MISSED",
|
|
17
17
|
"CANCELLED"
|
|
18
|
-
],
|
|
18
|
+
], C = [
|
|
19
19
|
"GetMilestonesForDetail",
|
|
20
20
|
"GetMilestones",
|
|
21
21
|
"GetDashboardData",
|
|
22
22
|
"GetAnalyticsData"
|
|
23
|
-
],
|
|
23
|
+
], w = l`
|
|
24
24
|
mutation MilestoneControlsUpdate($id: ID!, $input: PlanmagnetUpdateMilestoneInput!) {
|
|
25
25
|
planmagnetUpdateMilestone(id: $id, input: $input) {
|
|
26
26
|
id
|
|
@@ -31,161 +31,164 @@ var x = [
|
|
|
31
31
|
completedAt
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
|
-
`,
|
|
34
|
+
`, T = l`
|
|
35
35
|
mutation MilestoneControlsDelete($id: ID!) {
|
|
36
36
|
planmagnetDeleteMilestone(id: $id)
|
|
37
37
|
}
|
|
38
38
|
`;
|
|
39
|
-
function
|
|
40
|
-
let
|
|
41
|
-
refetchQueries:
|
|
39
|
+
function E({ milestone: s }) {
|
|
40
|
+
let l = e(), [p, m] = u(w, {
|
|
41
|
+
refetchQueries: C,
|
|
42
42
|
awaitRefetchQueries: !0
|
|
43
|
-
}), [
|
|
44
|
-
|
|
43
|
+
}), [h, g] = u(T, {
|
|
44
|
+
refetchQueries: C.filter((e) => e !== "GetMilestonesForDetail"),
|
|
45
|
+
context: t
|
|
46
|
+
}), [_, v] = o(null), y = m.loading || g.loading, b = S.includes(s.status) ? S : [s.status, ...S], x = async (e) => {
|
|
47
|
+
v(null);
|
|
45
48
|
try {
|
|
46
|
-
await
|
|
47
|
-
id:
|
|
49
|
+
await n(p({ variables: {
|
|
50
|
+
id: s.id,
|
|
48
51
|
input: { status: e }
|
|
49
52
|
} }));
|
|
50
53
|
} catch (e) {
|
|
51
|
-
|
|
54
|
+
v(e instanceof Error ? e.message : "The status could not be changed.");
|
|
52
55
|
}
|
|
53
|
-
},
|
|
54
|
-
|
|
56
|
+
}, E = async () => {
|
|
57
|
+
v(null);
|
|
55
58
|
try {
|
|
56
|
-
await
|
|
59
|
+
await n(h({ variables: { id: s.id } })), l("/projects/milestones-page");
|
|
57
60
|
} catch (e) {
|
|
58
|
-
|
|
61
|
+
v(e instanceof Error ? e.message : "The milestone could not be deleted.");
|
|
59
62
|
}
|
|
60
63
|
};
|
|
61
|
-
return /* @__PURE__ */
|
|
64
|
+
return /* @__PURE__ */ f("div", {
|
|
62
65
|
className: "flex flex-col items-end gap-2",
|
|
63
|
-
children: [/* @__PURE__ */
|
|
66
|
+
children: [/* @__PURE__ */ f("div", {
|
|
64
67
|
className: "flex flex-wrap items-center justify-end gap-2",
|
|
65
68
|
children: [
|
|
66
|
-
/* @__PURE__ */
|
|
69
|
+
/* @__PURE__ */ d("select", {
|
|
67
70
|
"aria-label": "Status",
|
|
68
71
|
"data-testid": "milestone-status",
|
|
69
72
|
className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
|
|
70
|
-
value:
|
|
71
|
-
disabled:
|
|
73
|
+
value: s.status,
|
|
74
|
+
disabled: y,
|
|
72
75
|
onChange: (e) => {
|
|
73
|
-
e.target.value !==
|
|
76
|
+
e.target.value !== s.status && x(e.target.value);
|
|
74
77
|
},
|
|
75
|
-
children:
|
|
78
|
+
children: b.map((e) => /* @__PURE__ */ d("option", {
|
|
76
79
|
value: e,
|
|
77
|
-
children:
|
|
80
|
+
children: a(e)
|
|
78
81
|
}, e))
|
|
79
82
|
}),
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
/* @__PURE__ */
|
|
83
|
+
/* @__PURE__ */ d(O, { milestone: s }),
|
|
84
|
+
/* @__PURE__ */ d(r, {
|
|
82
85
|
label: "Delete",
|
|
83
86
|
variant: "outline",
|
|
84
|
-
icon: /* @__PURE__ */
|
|
85
|
-
title: `Delete ${
|
|
87
|
+
icon: /* @__PURE__ */ d(c, { className: "mr-1 h-4 w-4" }),
|
|
88
|
+
title: `Delete ${s.name}?`,
|
|
86
89
|
description: "The milestone is deleted. This cannot be undone here.",
|
|
87
90
|
confirmLabel: "Delete milestone",
|
|
88
91
|
destructive: !0,
|
|
89
|
-
disabled:
|
|
90
|
-
onConfirm: () => void
|
|
92
|
+
disabled: y,
|
|
93
|
+
onConfirm: () => void E()
|
|
91
94
|
})
|
|
92
95
|
]
|
|
93
|
-
}),
|
|
96
|
+
}), _ && /* @__PURE__ */ d(i, { message: _ })]
|
|
94
97
|
});
|
|
95
98
|
}
|
|
96
|
-
var
|
|
97
|
-
function
|
|
98
|
-
let
|
|
99
|
+
var D = (e) => e ? e.slice(0, 10) : "";
|
|
100
|
+
function O({ milestone: e }) {
|
|
101
|
+
let t = {
|
|
99
102
|
name: e.name,
|
|
100
103
|
description: e.description ?? "",
|
|
101
|
-
dueDate:
|
|
102
|
-
}, [
|
|
103
|
-
refetchQueries:
|
|
104
|
+
dueDate: D(e.dueDate)
|
|
105
|
+
}, [r, a] = o(!1), [c, l] = o(t), [S, T] = o(null), [E, O] = u(w, {
|
|
106
|
+
refetchQueries: C,
|
|
104
107
|
awaitRefetchQueries: !0
|
|
105
108
|
}), k = async () => {
|
|
106
109
|
T(null);
|
|
107
110
|
let r = {};
|
|
108
|
-
if (c.name.trim() !==
|
|
109
|
-
|
|
111
|
+
if (c.name.trim() !== t.name && (r.name = c.name.trim()), c.description.trim() !== t.description.trim() && (r.description = c.description.trim() || null), c.dueDate !== t.dueDate && (r.dueDate = c.dueDate || null), Object.keys(r).length === 0) {
|
|
112
|
+
a(!1);
|
|
110
113
|
return;
|
|
111
114
|
}
|
|
112
115
|
try {
|
|
113
|
-
await
|
|
116
|
+
await n(E({ variables: {
|
|
114
117
|
id: e.id,
|
|
115
118
|
input: r
|
|
116
|
-
} })),
|
|
119
|
+
} })), a(!1);
|
|
117
120
|
} catch (e) {
|
|
118
121
|
T(e instanceof Error ? e.message : "The milestone could not be saved.");
|
|
119
122
|
}
|
|
120
123
|
};
|
|
121
|
-
return /* @__PURE__ */
|
|
122
|
-
open:
|
|
124
|
+
return /* @__PURE__ */ f(h, {
|
|
125
|
+
open: r,
|
|
123
126
|
onOpenChange: (e) => {
|
|
124
|
-
|
|
127
|
+
a(e), e ? l(t) : T(null);
|
|
125
128
|
},
|
|
126
|
-
children: [/* @__PURE__ */
|
|
129
|
+
children: [/* @__PURE__ */ d(x, {
|
|
127
130
|
asChild: !0,
|
|
128
|
-
children: /* @__PURE__ */
|
|
131
|
+
children: /* @__PURE__ */ f(p, {
|
|
129
132
|
size: "sm",
|
|
130
133
|
variant: "outline",
|
|
131
134
|
"data-testid": "milestone-edit",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ d(s, { className: "mr-1 h-4 w-4" }), "Edit"]
|
|
133
136
|
})
|
|
134
|
-
}), /* @__PURE__ */
|
|
137
|
+
}), /* @__PURE__ */ f(g, {
|
|
135
138
|
className: "max-w-lg",
|
|
136
139
|
children: [
|
|
137
|
-
/* @__PURE__ */
|
|
138
|
-
/* @__PURE__ */
|
|
140
|
+
/* @__PURE__ */ f(y, { children: [/* @__PURE__ */ d(b, { children: "Edit milestone" }), /* @__PURE__ */ d(_, { children: "Change the name, description or due date." })] }),
|
|
141
|
+
/* @__PURE__ */ f("div", {
|
|
139
142
|
className: "space-y-3",
|
|
140
143
|
children: [
|
|
141
|
-
/* @__PURE__ */
|
|
144
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
142
145
|
htmlFor: "milestone-edit-name",
|
|
143
146
|
className: "text-sm font-medium",
|
|
144
147
|
children: "Name"
|
|
145
|
-
}), /* @__PURE__ */
|
|
148
|
+
}), /* @__PURE__ */ d(m, {
|
|
146
149
|
id: "milestone-edit-name",
|
|
147
150
|
className: "mt-1",
|
|
148
151
|
value: c.name,
|
|
149
|
-
onChange: (e) =>
|
|
152
|
+
onChange: (e) => l({
|
|
150
153
|
...c,
|
|
151
154
|
name: e.target.value
|
|
152
155
|
})
|
|
153
156
|
})] }),
|
|
154
|
-
/* @__PURE__ */
|
|
157
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
155
158
|
htmlFor: "milestone-edit-due",
|
|
156
159
|
className: "text-sm font-medium",
|
|
157
160
|
children: "Due date"
|
|
158
|
-
}), /* @__PURE__ */
|
|
161
|
+
}), /* @__PURE__ */ d(m, {
|
|
159
162
|
id: "milestone-edit-due",
|
|
160
163
|
type: "date",
|
|
161
164
|
className: "mt-1",
|
|
162
165
|
value: c.dueDate,
|
|
163
|
-
onChange: (e) =>
|
|
166
|
+
onChange: (e) => l({
|
|
164
167
|
...c,
|
|
165
168
|
dueDate: e.target.value
|
|
166
169
|
})
|
|
167
170
|
})] }),
|
|
168
|
-
/* @__PURE__ */
|
|
171
|
+
/* @__PURE__ */ f("div", { children: [/* @__PURE__ */ d("label", {
|
|
169
172
|
htmlFor: "milestone-edit-description",
|
|
170
173
|
className: "text-sm font-medium",
|
|
171
174
|
children: "Description"
|
|
172
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ d("textarea", {
|
|
173
176
|
id: "milestone-edit-description",
|
|
174
177
|
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]",
|
|
175
178
|
value: c.description,
|
|
176
|
-
onChange: (e) =>
|
|
179
|
+
onChange: (e) => l({
|
|
177
180
|
...c,
|
|
178
181
|
description: e.target.value
|
|
179
182
|
})
|
|
180
183
|
})] }),
|
|
181
|
-
|
|
184
|
+
S && /* @__PURE__ */ d(i, { message: S })
|
|
182
185
|
]
|
|
183
186
|
}),
|
|
184
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ f(v, { children: [/* @__PURE__ */ d(p, {
|
|
185
188
|
variant: "outline",
|
|
186
|
-
onClick: () =>
|
|
189
|
+
onClick: () => a(!1),
|
|
187
190
|
children: "Cancel"
|
|
188
|
-
}), /* @__PURE__ */
|
|
191
|
+
}), /* @__PURE__ */ d(p, {
|
|
189
192
|
onClick: () => void k(),
|
|
190
193
|
disabled: c.name.trim() === "" || O.loading,
|
|
191
194
|
children: O.loading ? "Saving…" : "Save"
|
|
@@ -195,6 +198,6 @@ function D({ milestone: e }) {
|
|
|
195
198
|
});
|
|
196
199
|
}
|
|
197
200
|
//#endregion
|
|
198
|
-
export {
|
|
201
|
+
export { E as MilestoneHeaderActions };
|
|
199
202
|
|
|
200
203
|
//# sourceMappingURL=MilestoneControls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"MilestoneControls.js","names":[],"sources":["../../../src/planmagnet/controls/MilestoneControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Pencil, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from './ConfirmButton';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Milestone controls (BOFF-7953/7963). A milestone was created Pending and could never move: no status control,\n * no edit, no delete — so the In progress / Completed / Missed / Cancelled tabs could only be empty, and the\n * \"Completed\" date stayed \"—\". Completing now also records the date (service, 2026-09-27).\n */\n\nexport const MILESTONE_STATUSES = [\n 'PENDING',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'MISSED',\n 'CANCELLED',\n] as const;\n\nconst MILESTONE_QUERIES = [\n 'GetMilestonesForDetail',\n 'GetMilestones',\n 'GetDashboardData',\n 'GetAnalyticsData',\n];\n\nconst UPDATE = gql`\n mutation MilestoneControlsUpdate($id: ID!, $input: PlanmagnetUpdateMilestoneInput!) {\n planmagnetUpdateMilestone(id: $id, input: $input) {\n id\n name\n description\n status\n dueDate\n completedAt\n }\n }\n`;\nconst DELETE = gql`\n mutation MilestoneControlsDelete($id: ID!) {\n planmagnetDeleteMilestone(id: $id)\n }\n`;\n\nexport interface MilestoneValues {\n id: string;\n name: string;\n description: string | null;\n status: string;\n dueDate: string | null;\n}\n\nexport function MilestoneHeaderActions({ milestone }: { milestone: MilestoneValues }) {\n const navigate = usePlanMagnetNavigate();\n const [update, updateState] = useMutation(UPDATE, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE, {\n refetchQueries: MILESTONE_QUERIES.filter((q) => q !== 'GetMilestonesForDetail'),\n });\n const [error, setError] = useState<string | null>(null);\n const busy = updateState.loading || removeState.loading;\n const statuses = (MILESTONE_STATUSES as readonly string[]).includes(milestone.status)\n ? MILESTONE_STATUSES\n : [milestone.status, ...MILESTONE_STATUSES];\n\n const onStatus = async (status: string) => {\n setError(null);\n try {\n await throwOnError(update({ variables: { id: milestone.id, input: { status } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: milestone.id } }));\n navigate('/projects/milestones-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be deleted.');\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Status\"\n data-testid=\"milestone-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={milestone.status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== milestone.status) void onStatus(e.target.value);\n }}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n <MilestoneEditDialog milestone={milestone} />\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${milestone.name}?`}\n description=\"The milestone is deleted. This cannot be undone here.\"\n confirmLabel=\"Delete milestone\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\nconst day = (iso: string | null) => (iso ? iso.slice(0, 10) : '');\n\n/** Name, description and due date. Only changed fields are sent; an emptied field is cleared. */\nfunction MilestoneEditDialog({ milestone }: { milestone: MilestoneValues }) {\n const initial = {\n name: milestone.name,\n description: milestone.description ?? '',\n dueDate: day(milestone.dueDate),\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const [update, state] = useMutation(UPDATE, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n\n const save = async () => {\n setError(null);\n const input: Record<string, unknown> = {};\n if (form.name.trim() !== initial.name) input.name = form.name.trim();\n if (form.description.trim() !== initial.description.trim())\n input.description = form.description.trim() || null;\n if (form.dueDate !== initial.dueDate) input.dueDate = form.dueDate || null;\n if (Object.keys(input).length === 0) {\n setOpen(false);\n return;\n }\n try {\n await throwOnError(update({ variables: { id: milestone.id, input } }));\n setOpen(false);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be saved.');\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(initial);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"milestone-edit\">\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit milestone</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Change the name, description or due date.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"milestone-edit-name\" className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id=\"milestone-edit-name\"\n className=\"mt-1\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-due\" className=\"text-sm font-medium\">\n Due date\n </label>\n <Input\n id=\"milestone-edit-due\"\n type=\"date\"\n className=\"mt-1\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-description\" className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id=\"milestone-edit-description\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={form.name.trim() === '' || state.loading}>\n {state.loading ? 'Saving…' : 'Save'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"MilestoneControls.js","names":[],"sources":["../../../src/planmagnet/controls/MilestoneControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { Pencil, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from './ConfirmButton';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Milestone controls (BOFF-7953/7963). A milestone was created Pending and could never move: no status control,\n * no edit, no delete — so the In progress / Completed / Missed / Cancelled tabs could only be empty, and the\n * \"Completed\" date stayed \"—\". Completing now also records the date (service, 2026-09-27).\n */\n\nexport const MILESTONE_STATUSES = [\n 'PENDING',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'MISSED',\n 'CANCELLED',\n] as const;\n\nconst MILESTONE_QUERIES = [\n 'GetMilestonesForDetail',\n 'GetMilestones',\n 'GetDashboardData',\n 'GetAnalyticsData',\n];\n\nconst UPDATE = gql`\n mutation MilestoneControlsUpdate($id: ID!, $input: PlanmagnetUpdateMilestoneInput!) {\n planmagnetUpdateMilestone(id: $id, input: $input) {\n id\n name\n description\n status\n dueDate\n completedAt\n }\n }\n`;\nconst DELETE = gql`\n mutation MilestoneControlsDelete($id: ID!) {\n planmagnetDeleteMilestone(id: $id)\n }\n`;\n\nexport interface MilestoneValues {\n id: string;\n name: string;\n description: string | null;\n status: string;\n dueDate: string | null;\n}\n\nexport function MilestoneHeaderActions({ milestone }: { milestone: MilestoneValues }) {\n const navigate = usePlanMagnetNavigate();\n const [update, updateState] = useMutation(UPDATE, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE, {\n refetchQueries: MILESTONE_QUERIES.filter((q) => q !== 'GetMilestonesForDetail'),\n context: LEAVES_PAGE_CONTEXT,\n });\n const [error, setError] = useState<string | null>(null);\n const busy = updateState.loading || removeState.loading;\n const statuses = (MILESTONE_STATUSES as readonly string[]).includes(milestone.status)\n ? MILESTONE_STATUSES\n : [milestone.status, ...MILESTONE_STATUSES];\n\n const onStatus = async (status: string) => {\n setError(null);\n try {\n await throwOnError(update({ variables: { id: milestone.id, input: { status } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: milestone.id } }));\n navigate('/projects/milestones-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be deleted.');\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Status\"\n data-testid=\"milestone-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={milestone.status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== milestone.status) void onStatus(e.target.value);\n }}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n <MilestoneEditDialog milestone={milestone} />\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${milestone.name}?`}\n description=\"The milestone is deleted. This cannot be undone here.\"\n confirmLabel=\"Delete milestone\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\nconst day = (iso: string | null) => (iso ? iso.slice(0, 10) : '');\n\n/** Name, description and due date. Only changed fields are sent; an emptied field is cleared. */\nfunction MilestoneEditDialog({ milestone }: { milestone: MilestoneValues }) {\n const initial = {\n name: milestone.name,\n description: milestone.description ?? '',\n dueDate: day(milestone.dueDate),\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const [update, state] = useMutation(UPDATE, {\n refetchQueries: MILESTONE_QUERIES,\n awaitRefetchQueries: true,\n });\n\n const save = async () => {\n setError(null);\n const input: Record<string, unknown> = {};\n if (form.name.trim() !== initial.name) input.name = form.name.trim();\n if (form.description.trim() !== initial.description.trim())\n input.description = form.description.trim() || null;\n if (form.dueDate !== initial.dueDate) input.dueDate = form.dueDate || null;\n if (Object.keys(input).length === 0) {\n setOpen(false);\n return;\n }\n try {\n await throwOnError(update({ variables: { id: milestone.id, input } }));\n setOpen(false);\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The milestone could not be saved.');\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(initial);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"milestone-edit\">\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit milestone</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Change the name, description or due date.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"milestone-edit-name\" className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id=\"milestone-edit-name\"\n className=\"mt-1\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-due\" className=\"text-sm font-medium\">\n Due date\n </label>\n <Input\n id=\"milestone-edit-due\"\n type=\"date\"\n className=\"mt-1\"\n value={form.dueDate}\n onChange={(e) => setForm({ ...form, dueDate: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"milestone-edit-description\" className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id=\"milestone-edit-description\"\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]\"\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={form.name.trim() === '' || state.loading}>\n {state.loading ? 'Saving…' : 'Save'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ProjectControls.tsx"],"names":[],"mappings":"AAWA;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB,kFAOnB,CAAC;AAyBX,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,IAAI,EACJ,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,
|
|
1
|
+
{"version":3,"file":"ProjectControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ProjectControls.tsx"],"names":[],"mappings":"AAWA;;;;;GAKG;AAEH,eAAO,MAAM,gBAAgB,kFAOnB,CAAC;AAyBX,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,IAAI,EACJ,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB,2CA6EA"}
|
|
@@ -1,104 +1,107 @@
|
|
|
1
1
|
import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { useState as
|
|
7
|
-
import { Pencil as
|
|
8
|
-
import { gql as
|
|
9
|
-
import { useMutation as
|
|
10
|
-
import { jsx as
|
|
11
|
-
import { Button as
|
|
2
|
+
import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
|
|
3
|
+
import { detailPath as r } from "../utils/rowNavigation.js";
|
|
4
|
+
import { ConfirmButton as i, ErrorLine as a } from "./ConfirmButton.js";
|
|
5
|
+
import { enumLabel as o } from "./WorkItemControls.js";
|
|
6
|
+
import { useState as s } from "react";
|
|
7
|
+
import { Pencil as c, Trash2 as l } from "lucide-react";
|
|
8
|
+
import { gql as u } from "@apollo/client";
|
|
9
|
+
import { useMutation as d } from "@apollo/client/react";
|
|
10
|
+
import { jsx as f, jsxs as p } from "react/jsx-runtime";
|
|
11
|
+
import { Button as m } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/planmagnet/controls/ProjectControls.tsx
|
|
13
|
-
var
|
|
13
|
+
var h = [
|
|
14
14
|
"PLANNING",
|
|
15
15
|
"ACTIVE",
|
|
16
16
|
"ON_HOLD",
|
|
17
17
|
"COMPLETED",
|
|
18
18
|
"CANCELLED",
|
|
19
19
|
"ARCHIVED"
|
|
20
|
-
],
|
|
20
|
+
], g = u`
|
|
21
21
|
mutation ProjectControlsStatus($id: ID!, $input: PlanmagnetUpdateProjectInput!) {
|
|
22
22
|
planmagnetUpdateProject(id: $id, input: $input) {
|
|
23
23
|
id
|
|
24
24
|
status
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
|
-
`,
|
|
27
|
+
`, _ = u`
|
|
28
28
|
mutation ProjectControlsDelete($id: ID!) {
|
|
29
29
|
planmagnetDeleteProject(id: $id)
|
|
30
30
|
}
|
|
31
|
-
`,
|
|
31
|
+
`, v = [
|
|
32
32
|
"GetProjects",
|
|
33
33
|
"GetActiveProjects",
|
|
34
34
|
"GetBoardProjects",
|
|
35
35
|
"GetDashboardData",
|
|
36
36
|
"AdminPlanmagnetProjects"
|
|
37
37
|
];
|
|
38
|
-
function
|
|
39
|
-
let
|
|
40
|
-
refetchQueries: ["GetProjectDetail", ...
|
|
38
|
+
function y({ projectId: u, name: y, status: b }) {
|
|
39
|
+
let x = e(), [S, C] = d(g, {
|
|
40
|
+
refetchQueries: ["GetProjectDetail", ...v],
|
|
41
41
|
awaitRefetchQueries: !0
|
|
42
|
-
}), [
|
|
43
|
-
|
|
42
|
+
}), [w, T] = d(_, {
|
|
43
|
+
refetchQueries: v,
|
|
44
|
+
context: t
|
|
45
|
+
}), [E, D] = s(null), O = C.loading || T.loading, k = h.includes(b) ? h : [b, ...h], A = async (e) => {
|
|
46
|
+
D(null);
|
|
44
47
|
try {
|
|
45
|
-
await
|
|
46
|
-
id:
|
|
48
|
+
await n(S({ variables: {
|
|
49
|
+
id: u,
|
|
47
50
|
input: { status: e }
|
|
48
51
|
} }));
|
|
49
52
|
} catch (e) {
|
|
50
|
-
|
|
53
|
+
D(e instanceof Error ? e.message : "The status could not be changed.");
|
|
51
54
|
}
|
|
52
|
-
},
|
|
53
|
-
|
|
55
|
+
}, j = async () => {
|
|
56
|
+
D(null);
|
|
54
57
|
try {
|
|
55
|
-
await
|
|
58
|
+
await n(w({ variables: { id: u } })), x("/projects/projects-page");
|
|
56
59
|
} catch (e) {
|
|
57
|
-
|
|
60
|
+
D(e instanceof Error ? e.message : "The project could not be deleted.");
|
|
58
61
|
}
|
|
59
62
|
};
|
|
60
|
-
return /* @__PURE__ */
|
|
63
|
+
return /* @__PURE__ */ p("div", {
|
|
61
64
|
className: "flex flex-col items-end gap-2",
|
|
62
|
-
children: [/* @__PURE__ */
|
|
65
|
+
children: [/* @__PURE__ */ p("div", {
|
|
63
66
|
className: "flex flex-wrap items-center justify-end gap-2",
|
|
64
67
|
children: [
|
|
65
|
-
/* @__PURE__ */
|
|
68
|
+
/* @__PURE__ */ f("select", {
|
|
66
69
|
"aria-label": "Project status",
|
|
67
70
|
"data-testid": "project-status",
|
|
68
71
|
className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
|
|
69
|
-
value:
|
|
70
|
-
disabled:
|
|
72
|
+
value: b,
|
|
73
|
+
disabled: O,
|
|
71
74
|
onChange: (e) => {
|
|
72
|
-
e.target.value !==
|
|
75
|
+
e.target.value !== b && A(e.target.value);
|
|
73
76
|
},
|
|
74
|
-
children:
|
|
77
|
+
children: k.map((e) => /* @__PURE__ */ f("option", {
|
|
75
78
|
value: e,
|
|
76
|
-
children:
|
|
79
|
+
children: o(e)
|
|
77
80
|
}, e))
|
|
78
81
|
}),
|
|
79
|
-
/* @__PURE__ */
|
|
82
|
+
/* @__PURE__ */ p(m, {
|
|
80
83
|
size: "sm",
|
|
81
84
|
variant: "outline",
|
|
82
85
|
"data-testid": "project-edit",
|
|
83
|
-
onClick: () =>
|
|
84
|
-
children: [/* @__PURE__ */
|
|
86
|
+
onClick: () => x(r("/projects/edit-project-page", u)),
|
|
87
|
+
children: [/* @__PURE__ */ f(c, { className: "mr-1 h-4 w-4" }), "Edit"]
|
|
85
88
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
89
|
+
/* @__PURE__ */ f(i, {
|
|
87
90
|
label: "Delete",
|
|
88
91
|
variant: "outline",
|
|
89
|
-
icon: /* @__PURE__ */
|
|
90
|
-
title: `Delete ${
|
|
92
|
+
icon: /* @__PURE__ */ f(l, { className: "mr-1 h-4 w-4" }),
|
|
93
|
+
title: `Delete ${y}?`,
|
|
91
94
|
description: "The project is deleted. Its tasks, sprints, boards, milestones and budgets disappear from every list, and so do releases that belong to no other project. This cannot be undone here.",
|
|
92
95
|
confirmLabel: "Delete project",
|
|
93
96
|
destructive: !0,
|
|
94
|
-
disabled:
|
|
95
|
-
onConfirm: () => void
|
|
97
|
+
disabled: O,
|
|
98
|
+
onConfirm: () => void j()
|
|
96
99
|
})
|
|
97
100
|
]
|
|
98
|
-
}),
|
|
101
|
+
}), E && /* @__PURE__ */ f(a, { message: E })]
|
|
99
102
|
});
|
|
100
103
|
}
|
|
101
104
|
//#endregion
|
|
102
|
-
export {
|
|
105
|
+
export { h as PROJECT_STATUSES, y as ProjectHeaderActions };
|
|
103
106
|
|
|
104
107
|
//# sourceMappingURL=ProjectControls.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectControls.js","names":[],"sources":["../../../src/planmagnet/controls/ProjectControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Pencil, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from './ConfirmButton';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Project controls (BOFF-7940). A new project starts in PLANNING and, until 2026-09-27, nothing in the UI could\n * move it: no status control, and the Edit Project page (which had no Status field) was linked from nowhere. So\n * Projects > Active, the Dashboard's project status card and the admin Bulk Archive never showed a project created\n * in the UI.\n */\n\nexport const PROJECT_STATUSES = [\n 'PLANNING',\n 'ACTIVE',\n 'ON_HOLD',\n 'COMPLETED',\n 'CANCELLED',\n 'ARCHIVED',\n] as const;\n\nconst UPDATE_PROJECT_STATUS = gql`\n mutation ProjectControlsStatus($id: ID!, $input: PlanmagnetUpdateProjectInput!) {\n planmagnetUpdateProject(id: $id, input: $input) {\n id\n status\n }\n }\n`;\nconst DELETE_PROJECT = gql`\n mutation ProjectControlsDelete($id: ID!) {\n planmagnetDeleteProject(id: $id)\n }\n`;\n\n/** Every query that lists projects or shows a project's status. */\nconst PROJECT_LIST_QUERIES = [\n 'GetProjects',\n 'GetActiveProjects',\n 'GetBoardProjects',\n 'GetDashboardData',\n 'AdminPlanmagnetProjects',\n];\n\nexport function ProjectHeaderActions({\n projectId,\n name,\n status,\n}: {\n projectId: string;\n name: string;\n status: string;\n}) {\n const navigate = usePlanMagnetNavigate();\n const [updateStatus, statusState] = useMutation(UPDATE_PROJECT_STATUS, {\n refetchQueries: ['GetProjectDetail', ...PROJECT_LIST_QUERIES],\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE_PROJECT, {\n refetchQueries: PROJECT_LIST_QUERIES,\n });\n const [error, setError] = useState<string | null>(null);\n const busy = statusState.loading || removeState.loading;\n const statuses = (PROJECT_STATUSES as readonly string[]).includes(status)\n ? PROJECT_STATUSES\n : [status, ...PROJECT_STATUSES];\n\n const onStatus = async (next: string) => {\n setError(null);\n try {\n await throwOnError(updateStatus({ variables: { id: projectId, input: { status: next } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: projectId } }));\n navigate('/projects/projects-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The project could not be deleted.');\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Project status\"\n data-testid=\"project-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== status) void onStatus(e.target.value);\n }}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"project-edit\"\n onClick={() => navigate(detailPath('/projects/edit-project-page', projectId))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${name}?`}\n description=\"The project is deleted. Its tasks, sprints, boards, milestones and budgets disappear from every list, and so do releases that belong to no other project. This cannot be undone here.\"\n confirmLabel=\"Delete project\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,IAAmB;CAC9B;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAwB,CAAG;;;;;;;GAQ3B,IAAiB,CAAG;;;;GAOpB,IAAuB;CAC3B;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAqB,EACnC,cACA,SACA,aAKC;CACD,IAAM,IAAW,GAAuB,EAClC,CAAC,GAAc,KAAe,EAAY,GAAuB;EACrE,gBAAgB,CAAC,oBAAoB,GAAG,EAAqB;EAC7D,qBAAqB;EACtB,CAAC,EACI,CAAC,GAAQ,KAAe,EAAY,GAAgB
|
|
1
|
+
{"version":3,"file":"ProjectControls.js","names":[],"sources":["../../../src/planmagnet/controls/ProjectControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport { Button } from '@burdenoff/fe-libs/ui';\nimport { Pencil, Trash2 } from 'lucide-react';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from './ConfirmButton';\nimport { enumLabel } from './WorkItemControls';\n\n/**\n * Project controls (BOFF-7940). A new project starts in PLANNING and, until 2026-09-27, nothing in the UI could\n * move it: no status control, and the Edit Project page (which had no Status field) was linked from nowhere. So\n * Projects > Active, the Dashboard's project status card and the admin Bulk Archive never showed a project created\n * in the UI.\n */\n\nexport const PROJECT_STATUSES = [\n 'PLANNING',\n 'ACTIVE',\n 'ON_HOLD',\n 'COMPLETED',\n 'CANCELLED',\n 'ARCHIVED',\n] as const;\n\nconst UPDATE_PROJECT_STATUS = gql`\n mutation ProjectControlsStatus($id: ID!, $input: PlanmagnetUpdateProjectInput!) {\n planmagnetUpdateProject(id: $id, input: $input) {\n id\n status\n }\n }\n`;\nconst DELETE_PROJECT = gql`\n mutation ProjectControlsDelete($id: ID!) {\n planmagnetDeleteProject(id: $id)\n }\n`;\n\n/** Every query that lists projects or shows a project's status. */\nconst PROJECT_LIST_QUERIES = [\n 'GetProjects',\n 'GetActiveProjects',\n 'GetBoardProjects',\n 'GetDashboardData',\n 'AdminPlanmagnetProjects',\n];\n\nexport function ProjectHeaderActions({\n projectId,\n name,\n status,\n}: {\n projectId: string;\n name: string;\n status: string;\n}) {\n const navigate = usePlanMagnetNavigate();\n const [updateStatus, statusState] = useMutation(UPDATE_PROJECT_STATUS, {\n refetchQueries: ['GetProjectDetail', ...PROJECT_LIST_QUERIES],\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE_PROJECT, {\n refetchQueries: PROJECT_LIST_QUERIES,\n context: LEAVES_PAGE_CONTEXT,\n });\n const [error, setError] = useState<string | null>(null);\n const busy = statusState.loading || removeState.loading;\n const statuses = (PROJECT_STATUSES as readonly string[]).includes(status)\n ? PROJECT_STATUSES\n : [status, ...PROJECT_STATUSES];\n\n const onStatus = async (next: string) => {\n setError(null);\n try {\n await throwOnError(updateStatus({ variables: { id: projectId, input: { status: next } } }));\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The status could not be changed.');\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: projectId } }));\n navigate('/projects/projects-page');\n } catch (e) {\n setError(e instanceof Error ? e.message : 'The project could not be deleted.');\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Project status\"\n data-testid=\"project-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== status) void onStatus(e.target.value);\n }}\n >\n {statuses.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n <Button\n size=\"sm\"\n variant=\"outline\"\n data-testid=\"project-edit\"\n onClick={() => navigate(detailPath('/projects/edit-project-page', projectId))}\n >\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete ${name}?`}\n description=\"The project is deleted. Its tasks, sprints, boards, milestones and budgets disappear from every list, and so do releases that belong to no other project. This cannot be undone here.\"\n confirmLabel=\"Delete project\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAa,IAAmB;CAC9B;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAwB,CAAG;;;;;;;GAQ3B,IAAiB,CAAG;;;;GAOpB,IAAuB;CAC3B;CACA;CACA;CACA;CACA;CACD;AAED,SAAgB,EAAqB,EACnC,cACA,SACA,aAKC;CACD,IAAM,IAAW,GAAuB,EAClC,CAAC,GAAc,KAAe,EAAY,GAAuB;EACrE,gBAAgB,CAAC,oBAAoB,GAAG,EAAqB;EAC7D,qBAAqB;EACtB,CAAC,EACI,CAAC,GAAQ,KAAe,EAAY,GAAgB;EACxD,gBAAgB;EAChB,SAAS;EACV,CAAC,EACI,CAAC,GAAO,KAAY,EAAwB,KAAK,EACjD,IAAO,EAAY,WAAW,EAAY,SAC1C,IAAY,EAAuC,SAAS,EAAO,GACrE,IACA,CAAC,GAAQ,GAAG,EAAiB,EAE3B,IAAW,OAAO,MAAiB;AACvC,IAAS,KAAK;AACd,MAAI;AACF,SAAM,EAAa,EAAa,EAAE,WAAW;IAAE,IAAI;IAAW,OAAO,EAAE,QAAQ,GAAM;IAAE,EAAE,CAAC,CAAC;WACpF,GAAG;AACV,KAAS,aAAa,QAAQ,EAAE,UAAU,mCAAmC;;IAG3E,IAAW,YAAY;AAC3B,IAAS,KAAK;AACd,MAAI;AAEF,GADA,MAAM,EAAa,EAAO,EAAE,WAAW,EAAE,IAAI,GAAW,EAAE,CAAC,CAAC,EAC5D,EAAS,0BAA0B;WAC5B,GAAG;AACV,KAAS,aAAa,QAAQ,EAAE,UAAU,oCAAoC;;;AAIlF,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,UAAD;KACE,cAAW;KACX,eAAY;KACZ,WAAU;KACV,OAAO;KACP,UAAU;KACV,WAAW,MAAM;AACf,MAAI,EAAE,OAAO,UAAU,KAAa,EAAS,EAAE,OAAO,MAAM;;eAG7D,EAAS,KAAK,MACb,kBAAC,UAAD;MAAgB,OAAO;gBACpB,EAAU,EAAE;MACN,EAFI,EAEJ,CACT;KACK,CAAA;IACT,kBAAC,GAAD;KACE,MAAK;KACL,SAAQ;KACR,eAAY;KACZ,eAAe,EAAS,EAAW,+BAA+B,EAAU,CAAC;eAJ/E,CAME,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,OAE5B;;IACT,kBAAC,GAAD;KACE,OAAM;KACN,SAAQ;KACR,MAAM,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA;KACzC,OAAO,UAAU,EAAK;KACtB,aAAY;KACZ,cAAa;KACb,aAAA;KACA,UAAU;KACV,iBAAiB,KAAK,GAAU;KAChC,CAAA;IACE;MACL,KAAS,kBAAC,GAAD,EAAW,SAAS,GAAS,CAAA,CACnC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReleaseControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ReleaseControls.tsx"],"names":[],"mappings":"AAWA;;;;;GAKG;AAEH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,2GASnB,CAAC;AA0BX,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,
|
|
1
|
+
{"version":3,"file":"ReleaseControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/controls/ReleaseControls.tsx"],"names":[],"mappings":"AAWA;;;;;GAKG;AAEH,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,2GASnB,CAAC;AA0BX,wBAAgB,oBAAoB,CAAC,EACnC,SAAS,EACT,OAAO,EACP,MAAM,GACP,EAAE;IACD,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB,2CA6EA"}
|