@burdenoff/microfe-planmagnet 2026.926.2 → 2026.927.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +14 -4
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +102 -100
- 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 +95 -94
- 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 +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +97 -93
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +84 -73
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +13 -8
- package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +129 -125
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +112 -109
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +72 -83
- 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 +103 -103
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +101 -99
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +127 -125
- 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 +8 -11
- 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 +67 -68
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js +1 -5
- package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js.map +1 -1
- package/dist/planmagnet/people/People.d.ts +18 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -1
- package/dist/planmagnet/people/People.js +26 -12
- package/dist/planmagnet/people/People.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import { throwOnError as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { throwOnError as n } from "../utils/mutationResult.js";
|
|
3
|
+
import { detailPath as r } from "../utils/rowNavigation.js";
|
|
4
|
+
import { endBeforeStart as i, parseIntField as a } from "../utils/formValidation.js";
|
|
5
|
+
import { useEntityId as o } from "../hooks/useEntityId.js";
|
|
6
|
+
import { SprintLifecycleActions as s } from "../sprints/SprintControls.js";
|
|
7
|
+
import { useMemo as c, useState as l } from "react";
|
|
8
|
+
import { ArrowLeft as u, Save as d } from "lucide-react";
|
|
9
|
+
import { gql as f } from "@apollo/client";
|
|
10
|
+
import { useMutation as p, useQuery as m } from "@apollo/client/react";
|
|
11
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
12
|
+
import { Button as _, Card as v, CardContent as y, CardHeader as b, CardTitle as x, Input as S } from "@burdenoff/fe-libs/ui";
|
|
12
13
|
//#region src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx
|
|
13
|
-
var
|
|
14
|
+
var C = f`
|
|
14
15
|
query GetSprintForEdit($id: ID!, $workspaceId: ID!) {
|
|
15
16
|
planmagnetSprint(id: $id) {
|
|
16
17
|
id
|
|
@@ -29,7 +30,7 @@ var x = u`
|
|
|
29
30
|
}
|
|
30
31
|
}
|
|
31
32
|
}
|
|
32
|
-
`,
|
|
33
|
+
`, w = f`
|
|
33
34
|
mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {
|
|
34
35
|
planmagnetUpdateSprint(id: $id, input: $input) {
|
|
35
36
|
id
|
|
@@ -42,201 +43,203 @@ var x = u`
|
|
|
42
43
|
}
|
|
43
44
|
}
|
|
44
45
|
`;
|
|
45
|
-
function
|
|
46
|
-
let { workspaceId:
|
|
46
|
+
function T() {
|
|
47
|
+
let { workspaceId: f } = e(), T = o("sprintId"), E = t(), { data: D, loading: O, error: k } = m(C, {
|
|
47
48
|
variables: {
|
|
48
|
-
id:
|
|
49
|
-
workspaceId:
|
|
49
|
+
id: T,
|
|
50
|
+
workspaceId: f ?? "default"
|
|
50
51
|
},
|
|
51
|
-
skip: !
|
|
52
|
-
}), [
|
|
53
|
-
name:
|
|
54
|
-
startDate:
|
|
55
|
-
endDate:
|
|
56
|
-
goal:
|
|
57
|
-
capacity:
|
|
58
|
-
}), [
|
|
59
|
-
...
|
|
60
|
-
...
|
|
61
|
-
},
|
|
52
|
+
skip: !T
|
|
53
|
+
}), [A, j] = l(null), M = D?.planmagnetSprint, N = D?.planmagnetProjects?.items ?? [], P = c(() => ({
|
|
54
|
+
name: M?.name ?? "",
|
|
55
|
+
startDate: M?.startDate ? M.startDate.split("T")[0] : "",
|
|
56
|
+
endDate: M?.endDate ? M.endDate.split("T")[0] : "",
|
|
57
|
+
goal: M?.goal ?? "",
|
|
58
|
+
capacity: M?.capacity?.toString() ?? ""
|
|
59
|
+
}), [M]), [F, I] = l({}), L = {
|
|
60
|
+
...P,
|
|
61
|
+
...F
|
|
62
|
+
}, R = (e) => I((t) => {
|
|
62
63
|
let n = { ...t };
|
|
63
|
-
for (let [t, r] of Object.entries(e)) r !==
|
|
64
|
+
for (let [t, r] of Object.entries(e)) r !== L[t] && (n[t] = r);
|
|
64
65
|
return n;
|
|
65
|
-
}), [
|
|
66
|
+
}), [z, B] = l(null), V = i(L.startDate, L.endDate), H = a(L.capacity, { min: 0 }), [U, { loading: W }] = p(w, { refetchQueries: [
|
|
66
67
|
"GetSprints",
|
|
67
68
|
"GetSprintDetail",
|
|
68
69
|
"GetSprintForEdit",
|
|
69
70
|
"GetDashboardData"
|
|
70
71
|
] });
|
|
71
|
-
return
|
|
72
|
+
return T ? O && !D ? /* @__PURE__ */ h("div", {
|
|
72
73
|
className: "flex items-center justify-center p-12",
|
|
73
|
-
children: /* @__PURE__ */
|
|
74
|
-
}) :
|
|
74
|
+
children: /* @__PURE__ */ h("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
75
|
+
}) : k ? /* @__PURE__ */ g("div", {
|
|
75
76
|
className: "p-6 text-destructive",
|
|
76
|
-
children: ["Error loading sprint: ",
|
|
77
|
-
}) :
|
|
77
|
+
children: ["Error loading sprint: ", k.message]
|
|
78
|
+
}) : M ? /* @__PURE__ */ g("div", {
|
|
78
79
|
className: "space-y-6 p-6 max-w-4xl",
|
|
79
80
|
children: [
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ g("div", {
|
|
81
82
|
className: "flex items-center justify-between",
|
|
82
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ g("div", {
|
|
83
84
|
className: "flex items-center gap-3",
|
|
84
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ h(_, {
|
|
85
86
|
variant: "ghost",
|
|
86
87
|
size: "sm",
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
"aria-label": "Back to the sprint",
|
|
89
|
+
onClick: () => E(r("/projects/planning-detail-page", T)),
|
|
90
|
+
children: /* @__PURE__ */ h(u, { className: "h-4 w-4" })
|
|
91
|
+
}), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
|
|
89
92
|
className: "text-2xl font-semibold",
|
|
90
93
|
children: "Edit Planning Session"
|
|
91
|
-
}), /* @__PURE__ */
|
|
94
|
+
}), /* @__PURE__ */ h("p", {
|
|
92
95
|
className: "text-sm text-muted-foreground",
|
|
93
|
-
children:
|
|
96
|
+
children: M.name
|
|
94
97
|
})] })]
|
|
95
|
-
}), /* @__PURE__ */
|
|
98
|
+
}), /* @__PURE__ */ g(_, {
|
|
96
99
|
size: "sm",
|
|
97
100
|
onClick: async () => {
|
|
98
|
-
if (!
|
|
99
|
-
|
|
101
|
+
if (!M || V || H.invalid) return;
|
|
102
|
+
B(null), j(null);
|
|
100
103
|
let e = {
|
|
101
|
-
...
|
|
102
|
-
...
|
|
104
|
+
...L.startDate !== P.startDate && L.startDate ? { startDate: L.startDate } : {},
|
|
105
|
+
...L.endDate !== P.endDate && L.endDate ? { endDate: L.endDate } : {}
|
|
103
106
|
};
|
|
104
107
|
try {
|
|
105
|
-
await
|
|
106
|
-
id:
|
|
108
|
+
await n(U({ variables: {
|
|
109
|
+
id: M.id,
|
|
107
110
|
input: {
|
|
108
|
-
name:
|
|
111
|
+
name: L.name || void 0,
|
|
109
112
|
...e,
|
|
110
|
-
goal:
|
|
111
|
-
capacity:
|
|
113
|
+
goal: L.goal || void 0,
|
|
114
|
+
capacity: H.value
|
|
112
115
|
}
|
|
113
|
-
} })),
|
|
116
|
+
} })), j("Sprint updated successfully."), I({});
|
|
114
117
|
} catch (e) {
|
|
115
|
-
|
|
118
|
+
B(e instanceof Error ? e.message : "The sprint could not be saved.");
|
|
116
119
|
}
|
|
117
120
|
},
|
|
118
|
-
disabled:
|
|
119
|
-
children: [/* @__PURE__ */
|
|
121
|
+
disabled: W || V || H.invalid,
|
|
122
|
+
children: [/* @__PURE__ */ h(d, { className: "mr-1 h-4 w-4" }), W ? "Saving..." : "Save Changes"]
|
|
120
123
|
})]
|
|
121
124
|
}),
|
|
122
|
-
|
|
125
|
+
A && /* @__PURE__ */ h("div", {
|
|
123
126
|
role: "status",
|
|
124
127
|
className: "p-3 rounded-md text-sm bg-muted text-status-success-text",
|
|
125
|
-
children:
|
|
128
|
+
children: A
|
|
126
129
|
}),
|
|
127
|
-
|
|
130
|
+
z && /* @__PURE__ */ g("div", {
|
|
128
131
|
role: "alert",
|
|
129
132
|
className: "p-3 rounded-md text-sm bg-destructive/10 text-destructive",
|
|
130
|
-
children: ["Error: ",
|
|
133
|
+
children: ["Error: ", z]
|
|
131
134
|
}),
|
|
132
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ g(v, { children: [/* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(x, { children: "Session Details" }) }), /* @__PURE__ */ g(y, {
|
|
133
136
|
className: "space-y-4",
|
|
134
137
|
children: [
|
|
135
|
-
/* @__PURE__ */
|
|
138
|
+
/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
136
139
|
className: "text-sm font-medium",
|
|
137
140
|
children: "Name"
|
|
138
|
-
}), /* @__PURE__ */
|
|
139
|
-
value:
|
|
140
|
-
onChange: (e) =>
|
|
141
|
-
...
|
|
141
|
+
}), /* @__PURE__ */ h(S, {
|
|
142
|
+
value: L.name,
|
|
143
|
+
onChange: (e) => R({
|
|
144
|
+
...L,
|
|
142
145
|
name: e.target.value
|
|
143
146
|
}),
|
|
144
147
|
className: "mt-1"
|
|
145
148
|
})] }),
|
|
146
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ g("div", {
|
|
147
150
|
className: "grid grid-cols-2 gap-4",
|
|
148
|
-
children: [/* @__PURE__ */
|
|
151
|
+
children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
149
152
|
className: "text-sm font-medium",
|
|
150
153
|
children: "Project"
|
|
151
|
-
}), /* @__PURE__ */
|
|
154
|
+
}), /* @__PURE__ */ h("div", {
|
|
152
155
|
className: "mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground",
|
|
153
|
-
children:
|
|
154
|
-
})] }), /* @__PURE__ */
|
|
156
|
+
children: N.find((e) => e.id === M.projectId)?.name ?? M.projectId
|
|
157
|
+
})] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
155
158
|
className: "text-sm font-medium",
|
|
156
159
|
children: "Status"
|
|
157
|
-
}), /* @__PURE__ */
|
|
160
|
+
}), /* @__PURE__ */ g("div", {
|
|
158
161
|
className: "mt-1 flex items-start justify-between gap-2",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
162
|
+
children: [/* @__PURE__ */ h("span", {
|
|
160
163
|
className: "rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground capitalize",
|
|
161
|
-
children:
|
|
162
|
-
}), /* @__PURE__ */
|
|
163
|
-
sprintId:
|
|
164
|
-
sprintName:
|
|
165
|
-
status:
|
|
164
|
+
children: M.status.toLowerCase().replace(/_/g, " ")
|
|
165
|
+
}), /* @__PURE__ */ h(s, {
|
|
166
|
+
sprintId: M.id,
|
|
167
|
+
sprintName: M.name,
|
|
168
|
+
status: M.status
|
|
166
169
|
})]
|
|
167
170
|
})] })]
|
|
168
171
|
}),
|
|
169
|
-
/* @__PURE__ */
|
|
172
|
+
/* @__PURE__ */ g("div", {
|
|
170
173
|
className: "grid grid-cols-2 gap-4",
|
|
171
|
-
children: [/* @__PURE__ */
|
|
174
|
+
children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
172
175
|
htmlFor: "sprint-start",
|
|
173
176
|
className: "text-sm font-medium",
|
|
174
177
|
children: "Start Date"
|
|
175
|
-
}), /* @__PURE__ */
|
|
178
|
+
}), /* @__PURE__ */ h(S, {
|
|
176
179
|
id: "sprint-start",
|
|
177
180
|
type: "date",
|
|
178
|
-
value:
|
|
179
|
-
max:
|
|
180
|
-
onChange: (e) =>
|
|
181
|
-
...
|
|
181
|
+
value: L.startDate,
|
|
182
|
+
max: L.endDate || void 0,
|
|
183
|
+
onChange: (e) => R({
|
|
184
|
+
...L,
|
|
182
185
|
startDate: e.target.value
|
|
183
186
|
}),
|
|
184
187
|
className: "mt-1"
|
|
185
|
-
})] }), /* @__PURE__ */
|
|
188
|
+
})] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
186
189
|
htmlFor: "sprint-end",
|
|
187
190
|
className: "text-sm font-medium",
|
|
188
191
|
children: "End Date"
|
|
189
|
-
}), /* @__PURE__ */
|
|
192
|
+
}), /* @__PURE__ */ h(S, {
|
|
190
193
|
id: "sprint-end",
|
|
191
194
|
type: "date",
|
|
192
|
-
value:
|
|
193
|
-
min:
|
|
194
|
-
onChange: (e) =>
|
|
195
|
-
...
|
|
195
|
+
value: L.endDate,
|
|
196
|
+
min: L.startDate || void 0,
|
|
197
|
+
onChange: (e) => R({
|
|
198
|
+
...L,
|
|
196
199
|
endDate: e.target.value
|
|
197
200
|
}),
|
|
198
|
-
"aria-invalid":
|
|
201
|
+
"aria-invalid": V,
|
|
199
202
|
className: "mt-1"
|
|
200
203
|
})] })]
|
|
201
204
|
}),
|
|
202
|
-
|
|
205
|
+
V && /* @__PURE__ */ h("p", {
|
|
203
206
|
role: "alert",
|
|
204
207
|
className: "text-sm text-destructive",
|
|
205
208
|
children: "The end date is before the start date."
|
|
206
209
|
}),
|
|
207
|
-
/* @__PURE__ */
|
|
210
|
+
/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
|
|
208
211
|
className: "text-sm font-medium",
|
|
209
212
|
children: "Goal"
|
|
210
|
-
}), /* @__PURE__ */
|
|
213
|
+
}), /* @__PURE__ */ h("textarea", {
|
|
211
214
|
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]",
|
|
212
|
-
value:
|
|
213
|
-
onChange: (e) =>
|
|
214
|
-
...
|
|
215
|
+
value: L.goal,
|
|
216
|
+
onChange: (e) => R({
|
|
217
|
+
...L,
|
|
215
218
|
goal: e.target.value
|
|
216
219
|
}),
|
|
217
220
|
placeholder: "Sprint goal..."
|
|
218
221
|
})] }),
|
|
219
|
-
/* @__PURE__ */
|
|
220
|
-
/* @__PURE__ */
|
|
222
|
+
/* @__PURE__ */ g("div", { children: [
|
|
223
|
+
/* @__PURE__ */ h("label", {
|
|
221
224
|
htmlFor: "sprint-capacity",
|
|
222
225
|
className: "text-sm font-medium",
|
|
223
226
|
children: "Capacity (story points)"
|
|
224
227
|
}),
|
|
225
|
-
/* @__PURE__ */
|
|
228
|
+
/* @__PURE__ */ h(S, {
|
|
226
229
|
id: "sprint-capacity",
|
|
227
230
|
type: "number",
|
|
228
231
|
min: 0,
|
|
229
232
|
step: 1,
|
|
230
|
-
value:
|
|
231
|
-
onChange: (e) =>
|
|
232
|
-
...
|
|
233
|
+
value: L.capacity,
|
|
234
|
+
onChange: (e) => R({
|
|
235
|
+
...L,
|
|
233
236
|
capacity: e.target.value
|
|
234
237
|
}),
|
|
235
|
-
"aria-invalid":
|
|
238
|
+
"aria-invalid": H.invalid,
|
|
236
239
|
className: "mt-1",
|
|
237
240
|
placeholder: "e.g. 40"
|
|
238
241
|
}),
|
|
239
|
-
|
|
242
|
+
H.invalid && /* @__PURE__ */ h("p", {
|
|
240
243
|
role: "alert",
|
|
241
244
|
className: "mt-1 text-xs text-destructive",
|
|
242
245
|
children: "Capacity must be a whole number, 0 or more."
|
|
@@ -245,15 +248,15 @@ function C() {
|
|
|
245
248
|
]
|
|
246
249
|
})] })
|
|
247
250
|
]
|
|
248
|
-
}) : /* @__PURE__ */
|
|
251
|
+
}) : /* @__PURE__ */ h("div", {
|
|
249
252
|
className: "p-6 text-muted-foreground",
|
|
250
253
|
children: "Planning session not found."
|
|
251
|
-
}) : /* @__PURE__ */
|
|
254
|
+
}) : /* @__PURE__ */ h("div", {
|
|
252
255
|
className: "p-6 text-muted-foreground",
|
|
253
256
|
children: "No planning session selected. Open one from the list."
|
|
254
257
|
});
|
|
255
258
|
}
|
|
256
259
|
//#endregion
|
|
257
|
-
export {
|
|
260
|
+
export { T as ProjectsPlanningEditPagePage };
|
|
258
261
|
|
|
259
262
|
//# sourceMappingURL=ProjectsPlanningEditPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ProjectsPlanningEditPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { SprintLifecycleActions } from '../sprints/SprintControls';\nimport { throwOnError } from '../utils/mutationResult';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\n\nconst GET_SPRINT_FOR_EDIT = gql`\n query GetSprintForEdit($id: ID!, $workspaceId: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n status\n startDate\n endDate\n goal\n capacity\n }\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst UPDATE_SPRINT = gql`\n mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {\n planmagnetUpdateSprint(id: $id, input: $input) {\n id\n name\n status\n startDate\n endDate\n goal\n capacity\n }\n }\n`;\n\nexport function ProjectsPlanningEditPagePage() {\n const { workspaceId } = usePlanMagnet();\n const sprintId = useEntityId('sprintId');\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_FOR_EDIT, {\n variables: { id: sprintId, workspaceId: workspaceId ?? 'default' },\n skip: !sprintId,\n });\n\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const sprint = data?.planmagnetSprint;\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const defaultForm = useMemo(\n () => ({\n name: sprint?.name ?? '',\n startDate: sprint?.startDate ? sprint.startDate.split('T')[0] : '',\n endDate: sprint?.endDate ? sprint.endDate.split('T')[0] : '',\n goal: sprint?.goal ?? '',\n capacity: sprint?.capacity?.toString() ?? '',\n }),\n [sprint]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n // Keep ONLY the fields the user actually edited. Callers pass the whole form\n // ({ ...form, field }); copying every field made untouched dates stale\n // snapshots, so after a refetch (e.g. a CONFLICT) a retry re-sent the old\n // dates and silently reverted a teammate's replan (BOFF-7918 review).\n const setForm = (updates: Record<string, string>) =>\n setFormOverrides((prev) => {\n const next = { ...prev };\n for (const [key, value] of Object.entries(updates)) {\n if (value !== form[key as keyof typeof form]) next[key] = value;\n }\n return next;\n });\n\n const [saveError, setSaveError] = useState<string | null>(null);\n // BOFF-7833: the merged form is the plan the sprint would end up with (one side edited is compared with\n // the stored other side); the service enforces the same rules.\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n const capacity = parseIntField(form.capacity, { min: 0 });\n const [updateSprint, { loading: saving }] = useMutation(UPDATE_SPRINT, {\n refetchQueries: ['GetSprints', 'GetSprintDetail', 'GetSprintForEdit', 'GetDashboardData'],\n });\n\n const handleSave = async () => {\n if (!sprint || invalidDates || capacity.invalid) return;\n setSaveError(null);\n setSuccessMessage(null);\n // Send the planned dates only when the user changed them (BOFF-7918).\n // Every change to a planned date is recorded in the sprint's plan history,\n // so re-sending unchanged dates on a rename must not look like a replan.\n const dateChanges = {\n ...(form.startDate !== defaultForm.startDate && form.startDate\n ? { startDate: form.startDate }\n : {}),\n ...(form.endDate !== defaultForm.endDate && form.endDate ? { endDate: form.endDate } : {}),\n };\n try {\n await throwOnError(\n updateSprint({\n variables: {\n id: sprint.id,\n input: {\n name: form.name || undefined,\n ...dateChanges,\n goal: form.goal || undefined,\n capacity: capacity.value,\n },\n },\n })\n );\n // Only after the service ACCEPTED it: under fe-libs' errorPolicy 'all' a\n // refused save resolves, and onCompleted reported success for it.\n setSuccessMessage('Sprint updated successfully.');\n setFormOverrides({});\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The sprint could not be saved.');\n }\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-destructive\">Error loading sprint: {error.message}</div>;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button variant=\"ghost\" size=\"sm\">\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Planning Session</h1>\n <p className=\"text-sm text-muted-foreground\">{sprint.name}</p>\n </div>\n </div>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || invalidDates || capacity.invalid}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n\n {successMessage && (\n <div role=\"status\" className=\"p-3 rounded-md text-sm bg-muted text-status-success-text\">\n {successMessage}\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md text-sm bg-destructive/10 text-destructive\">\n Error: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Session Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Project</label>\n <div className=\"mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground\">\n {projects.find((p: { id: string; name: string }) => p.id === sprint.projectId)\n ?.name ?? sprint.projectId}\n </div>\n </div>\n <div>\n {/* Status is not an editable field: planmagnetUpdateSprint does not\n accept it, and sending it failed EVERY save on this page. It\n changes only through the guarded lifecycle actions. */}\n <label className=\"text-sm font-medium\">Status</label>\n <div className=\"mt-1 flex items-start justify-between gap-2\">\n <span className=\"rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground capitalize\">\n {sprint.status.toLowerCase().replace(/_/g, ' ')}\n </span>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"sprint-start\" className=\"text-sm font-medium\">\n Start Date\n </label>\n <Input\n id=\"sprint-start\"\n type=\"date\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-end\" className=\"text-sm font-medium\">\n End Date\n </label>\n <Input\n id=\"sprint-end\"\n type=\"date\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n aria-invalid={invalidDates}\n className=\"mt-1\"\n />\n </div>\n </div>\n {invalidDates && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n The end date is before the start date.\n </p>\n )}\n <div>\n <label className=\"text-sm font-medium\">Goal</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]\"\n value={form.goal}\n onChange={(e) => setForm({ ...form, goal: e.target.value })}\n placeholder=\"Sprint goal...\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-capacity\" className=\"text-sm font-medium\">\n Capacity (story points)\n </label>\n <Input\n id=\"sprint-capacity\"\n type=\"number\"\n min={0}\n step={1}\n value={form.capacity}\n onChange={(e) => setForm({ ...form, capacity: e.target.value })}\n aria-invalid={capacity.invalid}\n className=\"mt-1\"\n placeholder=\"e.g. 40\"\n />\n {capacity.invalid && (\n <p role=\"alert\" className=\"mt-1 text-xs text-destructive\">\n Capacity must be a whole number, 0 or more.\n </p>\n )}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ProjectsPlanningEditPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { SprintLifecycleActions } from '../sprints/SprintControls';\nimport { throwOnError } from '../utils/mutationResult';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\n\nconst GET_SPRINT_FOR_EDIT = gql`\n query GetSprintForEdit($id: ID!, $workspaceId: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n status\n startDate\n endDate\n goal\n capacity\n }\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst UPDATE_SPRINT = gql`\n mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {\n planmagnetUpdateSprint(id: $id, input: $input) {\n id\n name\n status\n startDate\n endDate\n goal\n capacity\n }\n }\n`;\n\nexport function ProjectsPlanningEditPagePage() {\n const { workspaceId } = usePlanMagnet();\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_FOR_EDIT, {\n variables: { id: sprintId, workspaceId: workspaceId ?? 'default' },\n skip: !sprintId,\n });\n\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const sprint = data?.planmagnetSprint;\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const defaultForm = useMemo(\n () => ({\n name: sprint?.name ?? '',\n startDate: sprint?.startDate ? sprint.startDate.split('T')[0] : '',\n endDate: sprint?.endDate ? sprint.endDate.split('T')[0] : '',\n goal: sprint?.goal ?? '',\n capacity: sprint?.capacity?.toString() ?? '',\n }),\n [sprint]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n // Keep ONLY the fields the user actually edited. Callers pass the whole form\n // ({ ...form, field }); copying every field made untouched dates stale\n // snapshots, so after a refetch (e.g. a CONFLICT) a retry re-sent the old\n // dates and silently reverted a teammate's replan (BOFF-7918 review).\n const setForm = (updates: Record<string, string>) =>\n setFormOverrides((prev) => {\n const next = { ...prev };\n for (const [key, value] of Object.entries(updates)) {\n if (value !== form[key as keyof typeof form]) next[key] = value;\n }\n return next;\n });\n\n const [saveError, setSaveError] = useState<string | null>(null);\n // BOFF-7833: the merged form is the plan the sprint would end up with (one side edited is compared with\n // the stored other side); the service enforces the same rules.\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n const capacity = parseIntField(form.capacity, { min: 0 });\n const [updateSprint, { loading: saving }] = useMutation(UPDATE_SPRINT, {\n refetchQueries: ['GetSprints', 'GetSprintDetail', 'GetSprintForEdit', 'GetDashboardData'],\n });\n\n const handleSave = async () => {\n if (!sprint || invalidDates || capacity.invalid) return;\n setSaveError(null);\n setSuccessMessage(null);\n // Send the planned dates only when the user changed them (BOFF-7918).\n // Every change to a planned date is recorded in the sprint's plan history,\n // so re-sending unchanged dates on a rename must not look like a replan.\n const dateChanges = {\n ...(form.startDate !== defaultForm.startDate && form.startDate\n ? { startDate: form.startDate }\n : {}),\n ...(form.endDate !== defaultForm.endDate && form.endDate ? { endDate: form.endDate } : {}),\n };\n try {\n await throwOnError(\n updateSprint({\n variables: {\n id: sprint.id,\n input: {\n name: form.name || undefined,\n ...dateChanges,\n goal: form.goal || undefined,\n capacity: capacity.value,\n },\n },\n })\n );\n // Only after the service ACCEPTED it: under fe-libs' errorPolicy 'all' a\n // refused save resolves, and onCompleted reported success for it.\n setSuccessMessage('Sprint updated successfully.');\n setFormOverrides({});\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The sprint could not be saved.');\n }\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-destructive\">Error loading sprint: {error.message}</div>;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Back to the sprint\"\n onClick={() => navigate(detailPath('/projects/planning-detail-page', sprintId))}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Planning Session</h1>\n <p className=\"text-sm text-muted-foreground\">{sprint.name}</p>\n </div>\n </div>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || invalidDates || capacity.invalid}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n\n {successMessage && (\n <div role=\"status\" className=\"p-3 rounded-md text-sm bg-muted text-status-success-text\">\n {successMessage}\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md text-sm bg-destructive/10 text-destructive\">\n Error: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Session Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Project</label>\n <div className=\"mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground\">\n {projects.find((p: { id: string; name: string }) => p.id === sprint.projectId)\n ?.name ?? sprint.projectId}\n </div>\n </div>\n <div>\n {/* Status is not an editable field: planmagnetUpdateSprint does not\n accept it, and sending it failed EVERY save on this page. It\n changes only through the guarded lifecycle actions. */}\n <label className=\"text-sm font-medium\">Status</label>\n <div className=\"mt-1 flex items-start justify-between gap-2\">\n <span className=\"rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground capitalize\">\n {sprint.status.toLowerCase().replace(/_/g, ' ')}\n </span>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"sprint-start\" className=\"text-sm font-medium\">\n Start Date\n </label>\n <Input\n id=\"sprint-start\"\n type=\"date\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-end\" className=\"text-sm font-medium\">\n End Date\n </label>\n <Input\n id=\"sprint-end\"\n type=\"date\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n aria-invalid={invalidDates}\n className=\"mt-1\"\n />\n </div>\n </div>\n {invalidDates && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n The end date is before the start date.\n </p>\n )}\n <div>\n <label className=\"text-sm font-medium\">Goal</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]\"\n value={form.goal}\n onChange={(e) => setForm({ ...form, goal: e.target.value })}\n placeholder=\"Sprint goal...\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-capacity\" className=\"text-sm font-medium\">\n Capacity (story points)\n </label>\n <Input\n id=\"sprint-capacity\"\n type=\"number\"\n min={0}\n step={1}\n value={form.capacity}\n onChange={(e) => setForm({ ...form, capacity: e.target.value })}\n aria-invalid={capacity.invalid}\n className=\"mt-1\"\n placeholder=\"e.g. 40\"\n />\n {capacity.invalid && (\n <p role=\"alert\" className=\"mt-1 text-xs text-destructive\">\n Capacity must be a whole number, 0 or more.\n </p>\n )}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportsReportsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ReportsReportsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ReportsReportsPagePage.tsx"],"names":[],"mappings":"AA6FA,wBAAgB,sBAAsB,4CAqKrC"}
|