@burdenoff/microfe-planmagnet 2026.924.5 → 2026.925.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/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +191 -172
- 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 +96 -104
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +59 -51
- package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +115 -105
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.d.ts +39 -0
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -0
- package/dist/planmagnet/sprints/SprintControls.js +522 -0
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SprintsStartSprintPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"SprintsStartSprintPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.tsx"],"names":[],"mappings":"AAiFA,wBAAgB,0BAA0B,4CAsNzC"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
1
|
+
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
|
+
import { detailPath as n } from "../utils/rowNavigation.js";
|
|
3
|
+
import { NewSprintDialog as r, SprintLifecycleActions as i } from "../sprints/SprintControls.js";
|
|
4
|
+
import { Calendar as a, Users as o, Zap as s } from "lucide-react";
|
|
5
|
+
import { gql as c } from "@apollo/client";
|
|
6
|
+
import { useQuery as l } from "@apollo/client/react";
|
|
7
|
+
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
8
|
+
import { Badge as f, Card as p, CardContent as m, CardHeader as h, CardTitle as g } from "@burdenoff/fe-libs/ui";
|
|
7
9
|
//#region src/planmagnet/pages/SprintsStartSprintPagePage.tsx
|
|
8
|
-
var
|
|
10
|
+
var _ = c`
|
|
9
11
|
query GetSprints(
|
|
10
12
|
$workspaceId: ID!
|
|
11
13
|
$filter: PlanmagnetSprintFilter
|
|
@@ -31,14 +33,6 @@ var g = a`
|
|
|
31
33
|
totalCount
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
|
-
`, _ = a`
|
|
35
|
-
mutation StartSprint($sprintId: ID!) {
|
|
36
|
-
planmagnetStartSprint(id: $sprintId) {
|
|
37
|
-
id
|
|
38
|
-
status
|
|
39
|
-
startDate
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
36
|
`;
|
|
43
37
|
function v(e) {
|
|
44
38
|
let t = e.workItems ?? [], n = t.reduce((e, t) => e + (t.storyPoints ?? 0), 0), r = t.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0);
|
|
@@ -52,163 +46,176 @@ function v(e) {
|
|
|
52
46
|
};
|
|
53
47
|
}
|
|
54
48
|
function y() {
|
|
55
|
-
let { workspaceId:
|
|
49
|
+
let { workspaceId: c } = e(), { data: y, loading: b, error: x } = l(_, {
|
|
56
50
|
variables: {
|
|
57
|
-
workspaceId:
|
|
51
|
+
workspaceId: c,
|
|
58
52
|
pagination: { first: 50 }
|
|
59
53
|
},
|
|
60
|
-
skip: !
|
|
61
|
-
}), S = y?.planmagnetSprints?.items ?? [], C = S.filter((e) => e.status === "ACTIVE"), w = S.filter((e) => e.status === "PLANNING"),
|
|
62
|
-
|
|
63
|
-
};
|
|
64
|
-
return b ? /* @__PURE__ */ c("div", {
|
|
54
|
+
skip: !c
|
|
55
|
+
}), S = y?.planmagnetSprints?.items ?? [], C = S.filter((e) => e.status === "ACTIVE" || e.status === "REVIEW"), w = S.filter((e) => e.status === "PLANNING"), T = t(), E = (e) => T(n("/projects/planning-detail-page", e));
|
|
56
|
+
return b ? /* @__PURE__ */ u("div", {
|
|
65
57
|
className: "flex items-center justify-center p-12",
|
|
66
|
-
children: /* @__PURE__ */
|
|
67
|
-
}) : x ? /* @__PURE__ */
|
|
58
|
+
children: /* @__PURE__ */ u("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
59
|
+
}) : x ? /* @__PURE__ */ d("div", {
|
|
68
60
|
className: "p-6 text-red-500",
|
|
69
61
|
children: ["Error: ", x.message]
|
|
70
|
-
}) : /* @__PURE__ */
|
|
62
|
+
}) : /* @__PURE__ */ d("div", {
|
|
71
63
|
className: "space-y-6 p-6",
|
|
72
64
|
children: [
|
|
73
|
-
/* @__PURE__ */
|
|
74
|
-
className: "
|
|
75
|
-
children: "
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
65
|
+
/* @__PURE__ */ d("div", {
|
|
66
|
+
className: "flex items-center justify-between",
|
|
67
|
+
children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
|
|
68
|
+
className: "text-2xl font-semibold",
|
|
69
|
+
children: "Sprint Management"
|
|
70
|
+
}), /* @__PURE__ */ d("p", {
|
|
71
|
+
className: "text-sm text-muted-foreground",
|
|
72
|
+
children: [
|
|
73
|
+
S.length,
|
|
74
|
+
" sprints total · ",
|
|
75
|
+
C.length,
|
|
76
|
+
" active"
|
|
77
|
+
]
|
|
78
|
+
})] }), /* @__PURE__ */ u(r, { onCreated: E })]
|
|
79
|
+
}),
|
|
80
|
+
C.length > 0 && /* @__PURE__ */ d("div", {
|
|
86
81
|
className: "space-y-4",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
82
|
+
children: [/* @__PURE__ */ u("h2", {
|
|
88
83
|
className: "text-lg font-medium",
|
|
89
84
|
children: "Active Sprints"
|
|
90
85
|
}), C.map((e) => {
|
|
91
|
-
let
|
|
92
|
-
return /* @__PURE__ */
|
|
86
|
+
let t = v(e), n = e.workItems ?? [], r = t.totalPoints > 0 ? Math.round(t.completedPoints / t.totalPoints * 100) : 0;
|
|
87
|
+
return /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, {
|
|
93
88
|
className: "pb-2",
|
|
94
|
-
children: /* @__PURE__ */
|
|
89
|
+
children: /* @__PURE__ */ d("div", {
|
|
95
90
|
className: "flex items-center justify-between",
|
|
96
|
-
children: [/* @__PURE__ */
|
|
91
|
+
children: [/* @__PURE__ */ d(g, {
|
|
97
92
|
className: "text-base flex items-center gap-2",
|
|
98
|
-
children: [/* @__PURE__ */
|
|
99
|
-
|
|
93
|
+
children: [/* @__PURE__ */ u(s, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ u("button", {
|
|
94
|
+
type: "button",
|
|
95
|
+
className: "hover:underline text-left",
|
|
96
|
+
onClick: () => E(e.id),
|
|
97
|
+
children: e.name
|
|
98
|
+
})]
|
|
99
|
+
}), /* @__PURE__ */ d("div", {
|
|
100
|
+
className: "flex items-start gap-2",
|
|
101
|
+
children: [/* @__PURE__ */ u(f, { children: e.status === "REVIEW" ? "Review" : "Active" }), /* @__PURE__ */ u(i, {
|
|
102
|
+
sprintId: e.id,
|
|
103
|
+
sprintName: e.name,
|
|
104
|
+
status: e.status
|
|
105
|
+
})]
|
|
106
|
+
})]
|
|
100
107
|
})
|
|
101
|
-
}), /* @__PURE__ */
|
|
108
|
+
}), /* @__PURE__ */ d(m, {
|
|
102
109
|
className: "space-y-4",
|
|
103
110
|
children: [
|
|
104
|
-
/* @__PURE__ */
|
|
111
|
+
/* @__PURE__ */ u("p", {
|
|
105
112
|
className: "text-sm text-muted-foreground",
|
|
106
113
|
children: e.goal ?? ""
|
|
107
114
|
}),
|
|
108
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ d("div", {
|
|
109
116
|
className: "flex gap-4 text-xs text-muted-foreground",
|
|
110
|
-
children: [/* @__PURE__ */
|
|
117
|
+
children: [/* @__PURE__ */ d("span", {
|
|
111
118
|
className: "flex items-center gap-1",
|
|
112
119
|
children: [
|
|
113
|
-
/* @__PURE__ */
|
|
120
|
+
/* @__PURE__ */ u(a, { className: "h-3 w-3" }),
|
|
114
121
|
e.startDate ?? "?",
|
|
115
122
|
" - ",
|
|
116
123
|
e.endDate ?? "?"
|
|
117
124
|
]
|
|
118
|
-
}), /* @__PURE__ */
|
|
125
|
+
}), /* @__PURE__ */ d("span", {
|
|
119
126
|
className: "flex items-center gap-1",
|
|
120
127
|
children: [
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
|
|
128
|
+
/* @__PURE__ */ u(o, { className: "h-3 w-3" }),
|
|
129
|
+
t.teamMemberIds.length,
|
|
123
130
|
" members"
|
|
124
131
|
]
|
|
125
132
|
})]
|
|
126
133
|
}),
|
|
127
|
-
/* @__PURE__ */
|
|
134
|
+
/* @__PURE__ */ d("div", {
|
|
128
135
|
className: "grid grid-cols-3 gap-4 text-center",
|
|
129
136
|
children: [
|
|
130
|
-
/* @__PURE__ */
|
|
137
|
+
/* @__PURE__ */ d("div", {
|
|
131
138
|
className: "p-3 bg-muted rounded-md",
|
|
132
|
-
children: [/* @__PURE__ */
|
|
139
|
+
children: [/* @__PURE__ */ d("p", {
|
|
133
140
|
className: "text-lg font-bold",
|
|
134
141
|
children: [
|
|
135
|
-
|
|
142
|
+
t.completedPoints,
|
|
136
143
|
"/",
|
|
137
|
-
|
|
144
|
+
t.totalPoints
|
|
138
145
|
]
|
|
139
|
-
}), /* @__PURE__ */
|
|
146
|
+
}), /* @__PURE__ */ u("p", {
|
|
140
147
|
className: "text-xs text-muted-foreground",
|
|
141
148
|
children: "Points"
|
|
142
149
|
})]
|
|
143
150
|
}),
|
|
144
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ d("div", {
|
|
145
152
|
className: "p-3 bg-muted rounded-md",
|
|
146
|
-
children: [/* @__PURE__ */
|
|
153
|
+
children: [/* @__PURE__ */ d("p", {
|
|
147
154
|
className: "text-lg font-bold",
|
|
148
155
|
children: [
|
|
149
|
-
|
|
156
|
+
t.tasksCompleted,
|
|
150
157
|
"/",
|
|
151
|
-
|
|
158
|
+
t.tasksTotal
|
|
152
159
|
]
|
|
153
|
-
}), /* @__PURE__ */
|
|
160
|
+
}), /* @__PURE__ */ u("p", {
|
|
154
161
|
className: "text-xs text-muted-foreground",
|
|
155
162
|
children: "Tasks"
|
|
156
163
|
})]
|
|
157
164
|
}),
|
|
158
|
-
/* @__PURE__ */
|
|
165
|
+
/* @__PURE__ */ d("div", {
|
|
159
166
|
className: "p-3 bg-muted rounded-md",
|
|
160
|
-
children: [/* @__PURE__ */
|
|
167
|
+
children: [/* @__PURE__ */ u("p", {
|
|
161
168
|
className: "text-lg font-bold",
|
|
162
|
-
children:
|
|
163
|
-
}), /* @__PURE__ */
|
|
169
|
+
children: t.velocity
|
|
170
|
+
}), /* @__PURE__ */ u("p", {
|
|
164
171
|
className: "text-xs text-muted-foreground",
|
|
165
172
|
children: "Velocity"
|
|
166
173
|
})]
|
|
167
174
|
})
|
|
168
175
|
]
|
|
169
176
|
}),
|
|
170
|
-
/* @__PURE__ */
|
|
177
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ d("div", {
|
|
171
178
|
className: "flex justify-between text-xs mb-1",
|
|
172
|
-
children: [/* @__PURE__ */
|
|
179
|
+
children: [/* @__PURE__ */ u("span", {
|
|
173
180
|
className: "text-muted-foreground",
|
|
174
181
|
children: "Sprint Progress"
|
|
175
|
-
}), /* @__PURE__ */
|
|
182
|
+
}), /* @__PURE__ */ d("span", {
|
|
176
183
|
className: "font-medium",
|
|
177
|
-
children: [
|
|
184
|
+
children: [r, "%"]
|
|
178
185
|
})]
|
|
179
|
-
}), /* @__PURE__ */
|
|
186
|
+
}), /* @__PURE__ */ u("div", {
|
|
180
187
|
className: "w-full h-2 rounded-full bg-muted overflow-hidden",
|
|
181
|
-
children: /* @__PURE__ */
|
|
188
|
+
children: /* @__PURE__ */ u("div", {
|
|
182
189
|
className: "h-full bg-primary rounded-full",
|
|
183
|
-
style: { width: `${
|
|
190
|
+
style: { width: `${r}%` }
|
|
184
191
|
})
|
|
185
192
|
})] }),
|
|
186
|
-
|
|
193
|
+
n.length > 0 && /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
187
194
|
className: "text-xs font-medium text-muted-foreground mb-2",
|
|
188
195
|
children: "Sprint Backlog"
|
|
189
|
-
}), /* @__PURE__ */
|
|
196
|
+
}), /* @__PURE__ */ u("div", {
|
|
190
197
|
className: "space-y-1",
|
|
191
|
-
children:
|
|
198
|
+
children: n.map((e) => /* @__PURE__ */ d("div", {
|
|
192
199
|
className: "flex items-center justify-between border rounded p-2 text-sm",
|
|
193
|
-
children: [/* @__PURE__ */
|
|
200
|
+
children: [/* @__PURE__ */ u("span", { children: e.title }), /* @__PURE__ */ d("div", {
|
|
194
201
|
className: "flex gap-1",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
202
|
+
children: [/* @__PURE__ */ u(f, {
|
|
196
203
|
variant: "secondary",
|
|
197
204
|
className: "text-xs capitalize",
|
|
198
205
|
children: e.status.toLowerCase().replace("_", " ")
|
|
199
|
-
}), /* @__PURE__ */
|
|
206
|
+
}), /* @__PURE__ */ d("span", {
|
|
200
207
|
className: "text-xs text-muted-foreground",
|
|
201
208
|
children: [e.storyPoints ?? 0, "pts"]
|
|
202
209
|
})]
|
|
203
210
|
})]
|
|
204
211
|
}, e.id))
|
|
205
212
|
})] }),
|
|
206
|
-
/* @__PURE__ */
|
|
213
|
+
/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
|
|
207
214
|
className: "text-xs font-medium text-muted-foreground mb-1",
|
|
208
215
|
children: "Team"
|
|
209
|
-
}), /* @__PURE__ */
|
|
216
|
+
}), /* @__PURE__ */ u("div", {
|
|
210
217
|
className: "flex gap-2",
|
|
211
|
-
children:
|
|
218
|
+
children: t.teamMemberIds.map((e) => /* @__PURE__ */ u("div", {
|
|
212
219
|
className: "h-7 w-7 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-medium text-primary",
|
|
213
220
|
title: e,
|
|
214
221
|
children: e.slice(0, 2).toUpperCase()
|
|
@@ -218,49 +225,52 @@ function y() {
|
|
|
218
225
|
})] }, e.id);
|
|
219
226
|
})]
|
|
220
227
|
}),
|
|
221
|
-
w.length > 0 && /* @__PURE__ */
|
|
228
|
+
w.length > 0 && /* @__PURE__ */ d("div", {
|
|
222
229
|
className: "space-y-4",
|
|
223
|
-
children: [/* @__PURE__ */
|
|
230
|
+
children: [/* @__PURE__ */ u("h2", {
|
|
224
231
|
className: "text-lg font-medium",
|
|
225
232
|
children: "Planning"
|
|
226
233
|
}), w.map((e) => {
|
|
227
234
|
let t = v(e);
|
|
228
|
-
return /* @__PURE__ */
|
|
235
|
+
return /* @__PURE__ */ d(p, { children: [/* @__PURE__ */ u(h, {
|
|
229
236
|
className: "pb-2",
|
|
230
|
-
children: /* @__PURE__ */
|
|
237
|
+
children: /* @__PURE__ */ d("div", {
|
|
231
238
|
className: "flex items-center justify-between",
|
|
232
|
-
children: [/* @__PURE__ */
|
|
239
|
+
children: [/* @__PURE__ */ u(g, {
|
|
233
240
|
className: "text-base",
|
|
234
|
-
children:
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
241
|
+
children: /* @__PURE__ */ u("button", {
|
|
242
|
+
type: "button",
|
|
243
|
+
className: "hover:underline text-left",
|
|
244
|
+
onClick: () => E(e.id),
|
|
245
|
+
children: e.name
|
|
246
|
+
})
|
|
247
|
+
}), /* @__PURE__ */ d("div", {
|
|
248
|
+
className: "flex items-start gap-2",
|
|
249
|
+
children: [/* @__PURE__ */ u(f, {
|
|
238
250
|
variant: "secondary",
|
|
239
251
|
children: "Planning"
|
|
240
|
-
}), /* @__PURE__ */
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
disabled: E,
|
|
245
|
-
children: [/* @__PURE__ */ c(n, { className: "mr-1 h-3 w-3" }), "Start Sprint"]
|
|
252
|
+
}), /* @__PURE__ */ u(i, {
|
|
253
|
+
sprintId: e.id,
|
|
254
|
+
sprintName: e.name,
|
|
255
|
+
status: e.status
|
|
246
256
|
})]
|
|
247
257
|
})]
|
|
248
258
|
})
|
|
249
|
-
}), /* @__PURE__ */
|
|
259
|
+
}), /* @__PURE__ */ d(m, {
|
|
250
260
|
className: "space-y-2",
|
|
251
|
-
children: [/* @__PURE__ */
|
|
261
|
+
children: [/* @__PURE__ */ u("p", {
|
|
252
262
|
className: "text-sm text-muted-foreground",
|
|
253
263
|
children: e.goal ?? ""
|
|
254
|
-
}), /* @__PURE__ */
|
|
264
|
+
}), /* @__PURE__ */ d("div", {
|
|
255
265
|
className: "flex gap-4 text-xs text-muted-foreground",
|
|
256
266
|
children: [
|
|
257
|
-
/* @__PURE__ */
|
|
267
|
+
/* @__PURE__ */ d("span", { children: [
|
|
258
268
|
e.startDate ?? "?",
|
|
259
269
|
" - ",
|
|
260
270
|
e.endDate ?? "?"
|
|
261
271
|
] }),
|
|
262
|
-
/* @__PURE__ */
|
|
263
|
-
/* @__PURE__ */
|
|
272
|
+
/* @__PURE__ */ d("span", { children: [t.totalPoints, " points planned"] }),
|
|
273
|
+
/* @__PURE__ */ d("span", { children: [t.teamMemberIds.length, " members"] })
|
|
264
274
|
]
|
|
265
275
|
})]
|
|
266
276
|
})] }, e.id);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SprintsStartSprintPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Badge, Button } from '@burdenoff/fe-libs/ui';\nimport { Zap, Calendar, Users, Play } from 'lucide-react';\nimport { usePlanMagnet } from '../providers/PlanMagnetProvider';\n\nconst GET_SPRINTS = gql`\n query GetSprints(\n $workspaceId: ID!\n $filter: PlanmagnetSprintFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSprints(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n goal\n status\n startDate\n endDate\n velocity\n workItems {\n id\n title\n status\n storyPoints\n assigneeId\n }\n }\n totalCount\n }\n }\n`;\n\nconst START_SPRINT = gql`\n mutation StartSprint($sprintId: ID!) {\n planmagnetStartSprint(id: $sprintId) {\n id\n status\n startDate\n }\n }\n`;\n\ninterface SprintWorkItem {\n id: string;\n title: string;\n status: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n goal: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n velocity: number | null;\n workItems: SprintWorkItem[];\n}\n\ninterface SprintMetrics {\n totalPoints: number;\n completedPoints: number;\n tasksTotal: number;\n tasksCompleted: number;\n teamMemberIds: string[];\n velocity: number;\n}\n\nfunction deriveSprintMetrics(sprint: SprintNode): SprintMetrics {\n const items = sprint.workItems ?? [];\n const totalPoints = items.reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const completedPoints = items\n .filter((w) => w.status === 'DONE')\n .reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const tasksTotal = items.length;\n const tasksCompleted = items.filter((w) => w.status === 'DONE').length;\n const teamMemberIds = Array.from(\n new Set(items.map((w) => w.assigneeId).filter((id): id is string => Boolean(id)))\n );\n const velocity = sprint.velocity ?? completedPoints;\n return { totalPoints, completedPoints, tasksTotal, tasksCompleted, teamMemberIds, velocity };\n}\n\nexport function SprintsStartSprintPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n const {\n data: sprintData,\n loading: sprintsLoading,\n error: sprintsError,\n } = useQuery<{\n planmagnetSprints: { items: SprintNode[]; totalCount: number };\n }>(GET_SPRINTS, {\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const sprints = sprintData?.planmagnetSprints?.items ?? [];\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE');\n const planningSprints = sprints.filter((s) => s.status === 'PLANNING');\n\n const [startSprint, { loading: startingSprintLoading }] = useMutation(START_SPRINT, {\n refetchQueries: ['GetSprints'],\n });\n\n const handleStartSprint = async (sprintId: string) => {\n if (!workspaceId) return;\n await startSprint({ variables: { sprintId } });\n };\n\n if (sprintsLoading) {\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 }\n\n if (sprintsError) {\n return <div className=\"p-6 text-red-500\">Error: {sprintsError.message}</div>;\n }\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">Sprint Management</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprints.length} sprints total · {activeSprints.length} active\n </p>\n </div>\n\n {activeSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Active Sprints</h2>\n {activeSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n const tasksForSprint = sprint.workItems ?? [];\n const pctDone =\n metrics.totalPoints > 0\n ? Math.round((metrics.completedPoints / metrics.totalPoints) * 100)\n : 0;\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Zap className=\"h-4 w-4 text-primary\" />\n {sprint.name}\n </CardTitle>\n <Badge>Active</Badge>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {metrics.teamMemberIds.length} members\n </span>\n </div>\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.completedPoints}/{metrics.totalPoints}\n </p>\n <p className=\"text-xs text-muted-foreground\">Points</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.tasksCompleted}/{metrics.tasksTotal}\n </p>\n <p className=\"text-xs text-muted-foreground\">Tasks</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">{metrics.velocity}</p>\n <p className=\"text-xs text-muted-foreground\">Velocity</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Sprint Progress</span>\n <span className=\"font-medium\">{pctDone}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${pctDone}%` }}\n />\n </div>\n </div>\n {tasksForSprint.length > 0 && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Sprint Backlog\n </p>\n <div className=\"space-y-1\">\n {tasksForSprint.map((t) => (\n <div\n key={t.id}\n className=\"flex items-center justify-between border rounded p-2 text-sm\"\n >\n <span>{t.title}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {t.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">\n {t.storyPoints ?? 0}pts\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Team</p>\n <div className=\"flex gap-2\">\n {metrics.teamMemberIds.map((memberId) => (\n <div\n key={memberId}\n className=\"h-7 w-7 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-medium text-primary\"\n title={memberId}\n >\n {memberId.slice(0, 2).toUpperCase()}\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {planningSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Planning</h2>\n {planningSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">{sprint.name}</CardTitle>\n <div className=\"flex items-center gap-2\">\n <Badge variant=\"secondary\">Planning</Badge>\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => handleStartSprint(sprint.id)}\n disabled={startingSprintLoading}\n >\n <Play className=\"mr-1 h-3 w-3\" />\n Start Sprint\n </Button>\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span>\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span>{metrics.totalPoints} points planned</span>\n <span>{metrics.teamMemberIds.length} members</span>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SprintsStartSprintPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/SprintsStartSprintPagePage.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 { Zap, Calendar, Users } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { NewSprintDialog, SprintLifecycleActions } from '../sprints/SprintControls';\n\nconst GET_SPRINTS = gql`\n query GetSprints(\n $workspaceId: ID!\n $filter: PlanmagnetSprintFilter\n $pagination: PlanmagnetPaginationInput\n ) {\n planmagnetSprints(workspaceId: $workspaceId, filter: $filter, pagination: $pagination) {\n items {\n id\n name\n goal\n status\n startDate\n endDate\n velocity\n workItems {\n id\n title\n status\n storyPoints\n assigneeId\n }\n }\n totalCount\n }\n }\n`;\n\ninterface SprintWorkItem {\n id: string;\n title: string;\n status: string;\n storyPoints: number | null;\n assigneeId: string | null;\n}\n\ninterface SprintNode {\n id: string;\n name: string;\n goal: string | null;\n status: string;\n startDate: string | null;\n endDate: string | null;\n velocity: number | null;\n workItems: SprintWorkItem[];\n}\n\ninterface SprintMetrics {\n totalPoints: number;\n completedPoints: number;\n tasksTotal: number;\n tasksCompleted: number;\n teamMemberIds: string[];\n velocity: number;\n}\n\nfunction deriveSprintMetrics(sprint: SprintNode): SprintMetrics {\n const items = sprint.workItems ?? [];\n const totalPoints = items.reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const completedPoints = items\n .filter((w) => w.status === 'DONE')\n .reduce((sum, w) => sum + (w.storyPoints ?? 0), 0);\n const tasksTotal = items.length;\n const tasksCompleted = items.filter((w) => w.status === 'DONE').length;\n const teamMemberIds = Array.from(\n new Set(items.map((w) => w.assigneeId).filter((id): id is string => Boolean(id)))\n );\n const velocity = sprint.velocity ?? completedPoints;\n return { totalPoints, completedPoints, tasksTotal, tasksCompleted, teamMemberIds, velocity };\n}\n\nexport function SprintsStartSprintPagePage() {\n const { workspaceId } = usePlanMagnet();\n\n const {\n data: sprintData,\n loading: sprintsLoading,\n error: sprintsError,\n } = useQuery<{\n planmagnetSprints: { items: SprintNode[]; totalCount: number };\n }>(GET_SPRINTS, {\n variables: { workspaceId, pagination: { first: 50 } },\n skip: !workspaceId,\n });\n\n const sprints = sprintData?.planmagnetSprints?.items ?? [];\n // REVIEW is still in flight (it can be completed or cancelled), so it\n // belongs with the active sprints rather than vanishing from this page.\n const activeSprints = sprints.filter((s) => s.status === 'ACTIVE' || s.status === 'REVIEW');\n const planningSprints = sprints.filter((s) => s.status === 'PLANNING');\n\n const navigate = usePlanMagnetNavigate();\n const openSprint = (id: string) => navigate(detailPath('/projects/planning-detail-page', id));\n\n if (sprintsLoading) {\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 }\n\n if (sprintsError) {\n return <div className=\"p-6 text-red-500\">Error: {sprintsError.message}</div>;\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 Management</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprints.length} sprints total · {activeSprints.length} active\n </p>\n </div>\n <NewSprintDialog onCreated={openSprint} />\n </div>\n\n {activeSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Active Sprints</h2>\n {activeSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n const tasksForSprint = sprint.workItems ?? [];\n const pctDone =\n metrics.totalPoints > 0\n ? Math.round((metrics.completedPoints / metrics.totalPoints) * 100)\n : 0;\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base flex items-center gap-2\">\n <Zap className=\"h-4 w-4 text-primary\" />\n <button\n type=\"button\"\n className=\"hover:underline text-left\"\n onClick={() => openSprint(sprint.id)}\n >\n {sprint.name}\n </button>\n </CardTitle>\n <div className=\"flex items-start gap-2\">\n <Badge>{sprint.status === 'REVIEW' ? 'Review' : 'Active'}</Badge>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span className=\"flex items-center gap-1\">\n <Calendar className=\"h-3 w-3\" />\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span className=\"flex items-center gap-1\">\n <Users className=\"h-3 w-3\" />\n {metrics.teamMemberIds.length} members\n </span>\n </div>\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.completedPoints}/{metrics.totalPoints}\n </p>\n <p className=\"text-xs text-muted-foreground\">Points</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">\n {metrics.tasksCompleted}/{metrics.tasksTotal}\n </p>\n <p className=\"text-xs text-muted-foreground\">Tasks</p>\n </div>\n <div className=\"p-3 bg-muted rounded-md\">\n <p className=\"text-lg font-bold\">{metrics.velocity}</p>\n <p className=\"text-xs text-muted-foreground\">Velocity</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-xs mb-1\">\n <span className=\"text-muted-foreground\">Sprint Progress</span>\n <span className=\"font-medium\">{pctDone}%</span>\n </div>\n <div className=\"w-full h-2 rounded-full bg-muted overflow-hidden\">\n <div\n className=\"h-full bg-primary rounded-full\"\n style={{ width: `${pctDone}%` }}\n />\n </div>\n </div>\n {tasksForSprint.length > 0 && (\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-2\">\n Sprint Backlog\n </p>\n <div className=\"space-y-1\">\n {tasksForSprint.map((t) => (\n <div\n key={t.id}\n className=\"flex items-center justify-between border rounded p-2 text-sm\"\n >\n <span>{t.title}</span>\n <div className=\"flex gap-1\">\n <Badge variant=\"secondary\" className=\"text-xs capitalize\">\n {t.status.toLowerCase().replace('_', ' ')}\n </Badge>\n <span className=\"text-xs text-muted-foreground\">\n {t.storyPoints ?? 0}pts\n </span>\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n <div>\n <p className=\"text-xs font-medium text-muted-foreground mb-1\">Team</p>\n <div className=\"flex gap-2\">\n {metrics.teamMemberIds.map((memberId) => (\n <div\n key={memberId}\n className=\"h-7 w-7 rounded-full bg-primary/10 flex items-center justify-center text-[10px] font-medium text-primary\"\n title={memberId}\n >\n {memberId.slice(0, 2).toUpperCase()}\n </div>\n ))}\n </div>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n\n {planningSprints.length > 0 && (\n <div className=\"space-y-4\">\n <h2 className=\"text-lg font-medium\">Planning</h2>\n {planningSprints.map((sprint) => {\n const metrics = deriveSprintMetrics(sprint);\n return (\n <Card key={sprint.id}>\n <CardHeader className=\"pb-2\">\n <div className=\"flex items-center justify-between\">\n <CardTitle className=\"text-base\">\n <button\n type=\"button\"\n className=\"hover:underline text-left\"\n onClick={() => openSprint(sprint.id)}\n >\n {sprint.name}\n </button>\n </CardTitle>\n <div className=\"flex items-start gap-2\">\n <Badge variant=\"secondary\">Planning</Badge>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </CardHeader>\n <CardContent className=\"space-y-2\">\n <p className=\"text-sm text-muted-foreground\">{sprint.goal ?? ''}</p>\n <div className=\"flex gap-4 text-xs text-muted-foreground\">\n <span>\n {sprint.startDate ?? '?'} - {sprint.endDate ?? '?'}\n </span>\n <span>{metrics.totalPoints} points planned</span>\n <span>{metrics.teamMemberIds.length} members</span>\n </div>\n </CardContent>\n </Card>\n );\n })}\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sprint lifecycle + membership controls.
|
|
3
|
+
*
|
|
4
|
+
* The buttons mirror the service's transition table exactly
|
|
5
|
+
* (wspace-planmagnet-svc sprint.service.ts `TRANSITIONS`):
|
|
6
|
+
* start PLANNING -> ACTIVE
|
|
7
|
+
* complete ACTIVE | REVIEW -> COMPLETED
|
|
8
|
+
* cancel PLANNING|ACTIVE|REVIEW -> CANCELLED
|
|
9
|
+
* A COMPLETED or CANCELLED sprint is finished: no transitions, and its work
|
|
10
|
+
* items can no longer be added or removed. The server enforces all of this;
|
|
11
|
+
* the UI only hides what would be refused, and still surfaces a refusal (a
|
|
12
|
+
* concurrent change can make a shown button stale).
|
|
13
|
+
*/
|
|
14
|
+
export type SprintStatus = 'PLANNING' | 'ACTIVE' | 'REVIEW' | 'COMPLETED' | 'CANCELLED';
|
|
15
|
+
export type SprintAction = 'start' | 'complete' | 'cancel';
|
|
16
|
+
export declare function sprintActionsFor(status: string): SprintAction[];
|
|
17
|
+
export declare function isSprintFinished(status: string): boolean;
|
|
18
|
+
export declare function SprintLifecycleActions({ sprintId, sprintName, status, }: {
|
|
19
|
+
sprintId: string;
|
|
20
|
+
sprintName: string;
|
|
21
|
+
status: string;
|
|
22
|
+
}): import("react/jsx-runtime").JSX.Element | null;
|
|
23
|
+
export interface SprintWorkItem {
|
|
24
|
+
id: string;
|
|
25
|
+
key: string;
|
|
26
|
+
title?: string | null;
|
|
27
|
+
status: string;
|
|
28
|
+
storyPoints: number | null;
|
|
29
|
+
}
|
|
30
|
+
export declare function SprintWorkItemsPanel({ sprintId, projectId, status, items, }: {
|
|
31
|
+
sprintId: string;
|
|
32
|
+
projectId: string;
|
|
33
|
+
status: string;
|
|
34
|
+
items: SprintWorkItem[];
|
|
35
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
36
|
+
export declare function NewSprintDialog({ onCreated }: {
|
|
37
|
+
onCreated: (sprintId: string) => void;
|
|
38
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
39
|
+
//# sourceMappingURL=SprintControls.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SprintControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/sprints/SprintControls.tsx"],"names":[],"mappings":"AA4BA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAC;AACxF,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,UAAU,GAAG,QAAQ,CAAC;AAQ3D,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,EAAE,CAI/D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAExD;AAsCD,wBAAgB,sBAAsB,CAAC,EACrC,QAAQ,EACR,UAAU,EACV,MAAM,GACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB,kDAyDA;AAsFD,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B;AAQD,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,SAAS,EACT,MAAM,EACN,KAAK,GACN,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB,2CAyEA;AAmKD,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE,2CAqKvF"}
|