@burdenoff/microfe-planmagnet 2026.925.18 → 2026.926.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/generated/wspace-types.d.ts +61 -2
- package/dist/generated/wspace-types.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +90 -79
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +124 -127
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
- package/dist/planmagnet/create/CreateDialogs.js +149 -124
- package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/NotFoundPage.d.ts +6 -0
- package/dist/planmagnet/pages/NotFoundPage.d.ts.map +1 -0
- package/dist/planmagnet/pages/NotFoundPage.js +18 -0
- package/dist/planmagnet/pages/NotFoundPage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +93 -98
- package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +72 -79
- package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js +112 -123
- package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +103 -87
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +92 -98
- 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 +88 -96
- package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +116 -114
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -88
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +79 -79
- package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +111 -91
- package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +117 -97
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +150 -154
- 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 +154 -117
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +75 -31
- package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +161 -115
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +117 -102
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +147 -147
- package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TeamTeamPagePage.js +77 -67
- package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
- package/dist/planmagnet/roadmaps/planningFocus.d.ts +23 -0
- package/dist/planmagnet/roadmaps/planningFocus.d.ts.map +1 -0
- package/dist/planmagnet/roadmaps/planningFocus.js +76 -0
- package/dist/planmagnet/roadmaps/planningFocus.js.map +1 -0
- package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
- package/dist/planmagnet/sprints/SprintControls.js +48 -29
- package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
- package/dist/planmagnet/sprints/SprintDates.d.ts +37 -0
- package/dist/planmagnet/sprints/SprintDates.d.ts.map +1 -0
- package/dist/planmagnet/sprints/SprintDates.js +114 -0
- package/dist/planmagnet/sprints/SprintDates.js.map +1 -0
- package/dist/planmagnet/utils/email.d.ts +9 -0
- package/dist/planmagnet/utils/email.d.ts.map +1 -0
- package/dist/planmagnet/utils/email.js +15 -0
- package/dist/planmagnet/utils/email.js.map +1 -0
- package/dist/planmagnet/utils/formValidation.d.ts +18 -0
- package/dist/planmagnet/utils/formValidation.d.ts.map +1 -0
- package/dist/planmagnet/utils/formValidation.js +17 -0
- package/dist/planmagnet/utils/formValidation.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
2
|
+
import { throwOnError as t } from "../utils/mutationResult.js";
|
|
3
|
+
import { endBeforeStart as n, parseIntField as r } from "../utils/formValidation.js";
|
|
4
|
+
import { useEntityId as i } from "../hooks/useEntityId.js";
|
|
5
|
+
import { SprintLifecycleActions as a } from "../sprints/SprintControls.js";
|
|
6
|
+
import { useMemo as o, useState as s } from "react";
|
|
7
|
+
import { ArrowLeft as c, Save as l } from "lucide-react";
|
|
8
|
+
import { gql as u } from "@apollo/client";
|
|
9
|
+
import { useMutation as d, useQuery as f } from "@apollo/client/react";
|
|
10
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
11
|
+
import { Button as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y, Input as b } from "@burdenoff/fe-libs/ui";
|
|
10
12
|
//#region src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx
|
|
11
|
-
var
|
|
13
|
+
var x = u`
|
|
12
14
|
query GetSprintForEdit($id: ID!, $workspaceId: ID!) {
|
|
13
15
|
planmagnetSprint(id: $id) {
|
|
14
16
|
id
|
|
@@ -27,7 +29,7 @@ var v = s`
|
|
|
27
29
|
}
|
|
28
30
|
}
|
|
29
31
|
}
|
|
30
|
-
`,
|
|
32
|
+
`, S = u`
|
|
31
33
|
mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {
|
|
32
34
|
planmagnetUpdateSprint(id: $id, input: $input) {
|
|
33
35
|
id
|
|
@@ -40,183 +42,218 @@ var v = s`
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
`;
|
|
43
|
-
function
|
|
44
|
-
let { workspaceId:
|
|
45
|
+
function C() {
|
|
46
|
+
let { workspaceId: u } = e(), C = i("sprintId"), { data: w, loading: T, error: E } = f(x, {
|
|
45
47
|
variables: {
|
|
46
|
-
id:
|
|
47
|
-
workspaceId:
|
|
48
|
+
id: C,
|
|
49
|
+
workspaceId: u ?? "default"
|
|
48
50
|
},
|
|
49
|
-
skip: !
|
|
50
|
-
}), [
|
|
51
|
-
name:
|
|
52
|
-
startDate:
|
|
53
|
-
endDate:
|
|
54
|
-
goal:
|
|
55
|
-
capacity:
|
|
56
|
-
}), [
|
|
57
|
-
...
|
|
58
|
-
...
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return b ? S && !x ? /* @__PURE__ */ u("div", {
|
|
51
|
+
skip: !C
|
|
52
|
+
}), [D, O] = s(null), k = w?.planmagnetSprint, A = w?.planmagnetProjects?.items ?? [], j = o(() => ({
|
|
53
|
+
name: k?.name ?? "",
|
|
54
|
+
startDate: k?.startDate ? k.startDate.split("T")[0] : "",
|
|
55
|
+
endDate: k?.endDate ? k.endDate.split("T")[0] : "",
|
|
56
|
+
goal: k?.goal ?? "",
|
|
57
|
+
capacity: k?.capacity?.toString() ?? ""
|
|
58
|
+
}), [k]), [M, N] = s({}), P = {
|
|
59
|
+
...j,
|
|
60
|
+
...M
|
|
61
|
+
}, F = (e) => N((t) => {
|
|
62
|
+
let n = { ...t };
|
|
63
|
+
for (let [t, r] of Object.entries(e)) r !== P[t] && (n[t] = r);
|
|
64
|
+
return n;
|
|
65
|
+
}), [I, L] = s(null), R = n(P.startDate, P.endDate), z = r(P.capacity, { min: 0 }), [B, { loading: V }] = d(S, { refetchQueries: [
|
|
66
|
+
"GetSprints",
|
|
67
|
+
"GetSprintDetail",
|
|
68
|
+
"GetSprintForEdit",
|
|
69
|
+
"GetDashboardData"
|
|
70
|
+
] });
|
|
71
|
+
return C ? T && !w ? /* @__PURE__ */ p("div", {
|
|
71
72
|
className: "flex items-center justify-center p-12",
|
|
72
|
-
children: /* @__PURE__ */
|
|
73
|
-
}) :
|
|
74
|
-
className: "p-6 text-
|
|
75
|
-
children: ["Error loading sprint: ",
|
|
76
|
-
}) :
|
|
73
|
+
children: /* @__PURE__ */ p("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
74
|
+
}) : E ? /* @__PURE__ */ m("div", {
|
|
75
|
+
className: "p-6 text-destructive",
|
|
76
|
+
children: ["Error loading sprint: ", E.message]
|
|
77
|
+
}) : k ? /* @__PURE__ */ m("div", {
|
|
77
78
|
className: "space-y-6 p-6 max-w-4xl",
|
|
78
79
|
children: [
|
|
79
|
-
/* @__PURE__ */
|
|
80
|
+
/* @__PURE__ */ m("div", {
|
|
80
81
|
className: "flex items-center justify-between",
|
|
81
|
-
children: [/* @__PURE__ */
|
|
82
|
+
children: [/* @__PURE__ */ m("div", {
|
|
82
83
|
className: "flex items-center gap-3",
|
|
83
|
-
children: [/* @__PURE__ */
|
|
84
|
+
children: [/* @__PURE__ */ p(h, {
|
|
84
85
|
variant: "ghost",
|
|
85
86
|
size: "sm",
|
|
86
|
-
children: /* @__PURE__ */
|
|
87
|
-
}), /* @__PURE__ */
|
|
87
|
+
children: /* @__PURE__ */ p(c, { className: "h-4 w-4" })
|
|
88
|
+
}), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
|
|
88
89
|
className: "text-2xl font-semibold",
|
|
89
90
|
children: "Edit Planning Session"
|
|
90
|
-
}), /* @__PURE__ */
|
|
91
|
+
}), /* @__PURE__ */ p("p", {
|
|
91
92
|
className: "text-sm text-muted-foreground",
|
|
92
|
-
children:
|
|
93
|
+
children: k.name
|
|
93
94
|
})] })]
|
|
94
|
-
}), /* @__PURE__ */
|
|
95
|
+
}), /* @__PURE__ */ m(h, {
|
|
95
96
|
size: "sm",
|
|
96
|
-
onClick: () => {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
97
|
+
onClick: async () => {
|
|
98
|
+
if (!k || R || z.invalid) return;
|
|
99
|
+
L(null), O(null);
|
|
100
|
+
let e = {
|
|
101
|
+
...P.startDate !== j.startDate && P.startDate ? { startDate: P.startDate } : {},
|
|
102
|
+
...P.endDate !== j.endDate && P.endDate ? { endDate: P.endDate } : {}
|
|
103
|
+
};
|
|
104
|
+
try {
|
|
105
|
+
await t(B({ variables: {
|
|
106
|
+
id: k.id,
|
|
107
|
+
input: {
|
|
108
|
+
name: P.name || void 0,
|
|
109
|
+
...e,
|
|
110
|
+
goal: P.goal || void 0,
|
|
111
|
+
capacity: z.value
|
|
112
|
+
}
|
|
113
|
+
} })), O("Sprint updated successfully."), N({});
|
|
114
|
+
} catch (e) {
|
|
115
|
+
L(e instanceof Error ? e.message : "The sprint could not be saved.");
|
|
116
|
+
}
|
|
107
117
|
},
|
|
108
|
-
disabled:
|
|
109
|
-
children: [/* @__PURE__ */
|
|
118
|
+
disabled: V || R || z.invalid,
|
|
119
|
+
children: [/* @__PURE__ */ p(l, { className: "mr-1 h-4 w-4" }), V ? "Saving..." : "Save Changes"]
|
|
110
120
|
})]
|
|
111
121
|
}),
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
122
|
+
D && /* @__PURE__ */ p("div", {
|
|
123
|
+
role: "status",
|
|
124
|
+
className: "p-3 rounded-md text-sm bg-muted text-status-success-text",
|
|
125
|
+
children: D
|
|
115
126
|
}),
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
127
|
+
I && /* @__PURE__ */ m("div", {
|
|
128
|
+
role: "alert",
|
|
129
|
+
className: "p-3 rounded-md text-sm bg-destructive/10 text-destructive",
|
|
130
|
+
children: ["Error: ", I]
|
|
119
131
|
}),
|
|
120
|
-
/* @__PURE__ */
|
|
132
|
+
/* @__PURE__ */ m(g, { children: [/* @__PURE__ */ p(v, { children: /* @__PURE__ */ p(y, { children: "Session Details" }) }), /* @__PURE__ */ m(_, {
|
|
121
133
|
className: "space-y-4",
|
|
122
134
|
children: [
|
|
123
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
124
136
|
className: "text-sm font-medium",
|
|
125
137
|
children: "Name"
|
|
126
|
-
}), /* @__PURE__ */
|
|
127
|
-
value:
|
|
128
|
-
onChange: (e) =>
|
|
129
|
-
...
|
|
138
|
+
}), /* @__PURE__ */ p(b, {
|
|
139
|
+
value: P.name,
|
|
140
|
+
onChange: (e) => F({
|
|
141
|
+
...P,
|
|
130
142
|
name: e.target.value
|
|
131
143
|
}),
|
|
132
144
|
className: "mt-1"
|
|
133
145
|
})] }),
|
|
134
|
-
/* @__PURE__ */
|
|
146
|
+
/* @__PURE__ */ m("div", {
|
|
135
147
|
className: "grid grid-cols-2 gap-4",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
148
|
+
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
137
149
|
className: "text-sm font-medium",
|
|
138
150
|
children: "Project"
|
|
139
|
-
}), /* @__PURE__ */
|
|
151
|
+
}), /* @__PURE__ */ p("div", {
|
|
140
152
|
className: "mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground",
|
|
141
|
-
children:
|
|
142
|
-
})] }), /* @__PURE__ */
|
|
153
|
+
children: A.find((e) => e.id === k.projectId)?.name ?? k.projectId
|
|
154
|
+
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
143
155
|
className: "text-sm font-medium",
|
|
144
156
|
children: "Status"
|
|
145
|
-
}), /* @__PURE__ */
|
|
157
|
+
}), /* @__PURE__ */ m("div", {
|
|
146
158
|
className: "mt-1 flex items-start justify-between gap-2",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ p("span", {
|
|
148
160
|
className: "rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground capitalize",
|
|
149
|
-
children:
|
|
150
|
-
}), /* @__PURE__ */
|
|
151
|
-
sprintId:
|
|
152
|
-
sprintName:
|
|
153
|
-
status:
|
|
161
|
+
children: k.status.toLowerCase().replace(/_/g, " ")
|
|
162
|
+
}), /* @__PURE__ */ p(a, {
|
|
163
|
+
sprintId: k.id,
|
|
164
|
+
sprintName: k.name,
|
|
165
|
+
status: k.status
|
|
154
166
|
})]
|
|
155
167
|
})] })]
|
|
156
168
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
169
|
+
/* @__PURE__ */ m("div", {
|
|
158
170
|
className: "grid grid-cols-2 gap-4",
|
|
159
|
-
children: [/* @__PURE__ */
|
|
171
|
+
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
172
|
+
htmlFor: "sprint-start",
|
|
160
173
|
className: "text-sm font-medium",
|
|
161
174
|
children: "Start Date"
|
|
162
|
-
}), /* @__PURE__ */
|
|
175
|
+
}), /* @__PURE__ */ p(b, {
|
|
176
|
+
id: "sprint-start",
|
|
163
177
|
type: "date",
|
|
164
|
-
value:
|
|
165
|
-
|
|
166
|
-
|
|
178
|
+
value: P.startDate,
|
|
179
|
+
max: P.endDate || void 0,
|
|
180
|
+
onChange: (e) => F({
|
|
181
|
+
...P,
|
|
167
182
|
startDate: e.target.value
|
|
168
183
|
}),
|
|
169
184
|
className: "mt-1"
|
|
170
|
-
})] }), /* @__PURE__ */
|
|
185
|
+
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
186
|
+
htmlFor: "sprint-end",
|
|
171
187
|
className: "text-sm font-medium",
|
|
172
188
|
children: "End Date"
|
|
173
|
-
}), /* @__PURE__ */
|
|
189
|
+
}), /* @__PURE__ */ p(b, {
|
|
190
|
+
id: "sprint-end",
|
|
174
191
|
type: "date",
|
|
175
|
-
value:
|
|
176
|
-
|
|
177
|
-
|
|
192
|
+
value: P.endDate,
|
|
193
|
+
min: P.startDate || void 0,
|
|
194
|
+
onChange: (e) => F({
|
|
195
|
+
...P,
|
|
178
196
|
endDate: e.target.value
|
|
179
197
|
}),
|
|
198
|
+
"aria-invalid": R,
|
|
180
199
|
className: "mt-1"
|
|
181
200
|
})] })]
|
|
182
201
|
}),
|
|
183
|
-
|
|
202
|
+
R && /* @__PURE__ */ p("p", {
|
|
203
|
+
role: "alert",
|
|
204
|
+
className: "text-sm text-destructive",
|
|
205
|
+
children: "The end date is before the start date."
|
|
206
|
+
}),
|
|
207
|
+
/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
184
208
|
className: "text-sm font-medium",
|
|
185
209
|
children: "Goal"
|
|
186
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ p("textarea", {
|
|
187
211
|
className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]",
|
|
188
|
-
value:
|
|
189
|
-
onChange: (e) =>
|
|
190
|
-
...
|
|
212
|
+
value: P.goal,
|
|
213
|
+
onChange: (e) => F({
|
|
214
|
+
...P,
|
|
191
215
|
goal: e.target.value
|
|
192
216
|
}),
|
|
193
217
|
placeholder: "Sprint goal..."
|
|
194
218
|
})] }),
|
|
195
|
-
/* @__PURE__ */
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
219
|
+
/* @__PURE__ */ m("div", { children: [
|
|
220
|
+
/* @__PURE__ */ p("label", {
|
|
221
|
+
htmlFor: "sprint-capacity",
|
|
222
|
+
className: "text-sm font-medium",
|
|
223
|
+
children: "Capacity (story points)"
|
|
224
|
+
}),
|
|
225
|
+
/* @__PURE__ */ p(b, {
|
|
226
|
+
id: "sprint-capacity",
|
|
227
|
+
type: "number",
|
|
228
|
+
min: 0,
|
|
229
|
+
step: 1,
|
|
230
|
+
value: P.capacity,
|
|
231
|
+
onChange: (e) => F({
|
|
232
|
+
...P,
|
|
233
|
+
capacity: e.target.value
|
|
234
|
+
}),
|
|
235
|
+
"aria-invalid": z.invalid,
|
|
236
|
+
className: "mt-1",
|
|
237
|
+
placeholder: "e.g. 40"
|
|
204
238
|
}),
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
239
|
+
z.invalid && /* @__PURE__ */ p("p", {
|
|
240
|
+
role: "alert",
|
|
241
|
+
className: "mt-1 text-xs text-destructive",
|
|
242
|
+
children: "Capacity must be a whole number, 0 or more."
|
|
243
|
+
})
|
|
244
|
+
] })
|
|
208
245
|
]
|
|
209
246
|
})] })
|
|
210
247
|
]
|
|
211
|
-
}) : /* @__PURE__ */
|
|
248
|
+
}) : /* @__PURE__ */ p("div", {
|
|
212
249
|
className: "p-6 text-muted-foreground",
|
|
213
250
|
children: "Planning session not found."
|
|
214
|
-
}) : /* @__PURE__ */
|
|
251
|
+
}) : /* @__PURE__ */ p("div", {
|
|
215
252
|
className: "p-6 text-muted-foreground",
|
|
216
253
|
children: "No planning session selected. Open one from the list."
|
|
217
254
|
});
|
|
218
255
|
}
|
|
219
256
|
//#endregion
|
|
220
|
-
export {
|
|
257
|
+
export { C as ProjectsPlanningEditPagePage };
|
|
221
258
|
|
|
222
259
|
//# 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';\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 const setForm = (updates: Record<string, string>) => setFormOverrides(updates);\n\n const [updateSprint, { loading: saving, error: saveError }] = useMutation(UPDATE_SPRINT, {\n refetchQueries: ['GetSprints', 'GetSprintDetail', 'GetSprintForEdit', 'GetDashboardData'],\n onCompleted: () => {\n setSuccessMessage('Sprint updated successfully.');\n },\n });\n\n const handleSave = () => {\n if (!sprint) return;\n updateSprint({\n variables: {\n id: sprint.id,\n input: {\n name: form.name || undefined,\n startDate: form.startDate || undefined,\n endDate: form.endDate || undefined,\n goal: form.goal || undefined,\n capacity: form.capacity ? parseInt(form.capacity, 10) : undefined,\n },\n },\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) return <div className=\"p-6 text-red-500\">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 size=\"sm\" onClick={handleSave} disabled={saving}>\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n\n {successMessage && (\n <div className=\"p-3 bg-green-50 text-green-800 rounded-md text-sm\">{successMessage}</div>\n )}\n {saveError && (\n <div className=\"p-3 bg-red-50 text-red-800 rounded-md text-sm\">\n Error: {saveError.message}\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 className=\"text-sm font-medium\">Start Date</label>\n <Input\n type=\"date\"\n value={form.startDate}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label className=\"text-sm font-medium\">End Date</label>\n <Input\n type=\"date\"\n value={form.endDate}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n </div>\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 className=\"text-sm font-medium\">Capacity (story points)</label>\n <Input\n type=\"number\"\n value={form.capacity}\n onChange={(e) => setForm({ ...form, capacity: e.target.value })}\n className=\"mt-1\"\n placeholder=\"e.g. 40\"\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 } 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 +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":"AAmGA,wBAAgB,0BAA0B,4CA2PzC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
|
|
2
2
|
import { detailPath as n } from "../utils/rowNavigation.js";
|
|
3
3
|
import { NewSprintDialog as r, SprintLifecycleActions as i } from "../sprints/SprintControls.js";
|
|
4
|
-
import {
|
|
4
|
+
import { SprintDates as a } from "../sprints/SprintDates.js";
|
|
5
|
+
import { Users as o, Zap as s } from "lucide-react";
|
|
5
6
|
import { gql as c } from "@apollo/client";
|
|
6
7
|
import { useQuery as l } from "@apollo/client/react";
|
|
7
8
|
import { jsx as u, jsxs as d } from "react/jsx-runtime";
|
|
@@ -21,6 +22,18 @@ var _ = c`
|
|
|
21
22
|
status
|
|
22
23
|
startDate
|
|
23
24
|
endDate
|
|
25
|
+
startedAt
|
|
26
|
+
completedAt
|
|
27
|
+
baselineStartDate
|
|
28
|
+
baselineEndDate
|
|
29
|
+
planChanges {
|
|
30
|
+
field
|
|
31
|
+
fromValue
|
|
32
|
+
toValue
|
|
33
|
+
sprintStatus
|
|
34
|
+
changedBy
|
|
35
|
+
changedAt
|
|
36
|
+
}
|
|
24
37
|
velocity
|
|
25
38
|
workItems {
|
|
26
39
|
id
|
|
@@ -52,7 +65,7 @@ function y() {
|
|
|
52
65
|
pagination: { first: 50 }
|
|
53
66
|
},
|
|
54
67
|
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 =
|
|
68
|
+
}), S = y?.planmagnetSprints?.items ?? [], C = S.filter((e) => e.status === "ACTIVE" || e.status === "REVIEW"), w = S.filter((e) => e.status === "PLANNING"), T = S.filter((e) => e.status === "COMPLETED" || e.status === "CANCELLED"), E = t(), D = (e) => E(n("/projects/planning-detail-page", e));
|
|
56
69
|
return b && !y ? /* @__PURE__ */ u("div", {
|
|
57
70
|
className: "flex items-center justify-center p-12",
|
|
58
71
|
children: /* @__PURE__ */ u("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
|
|
@@ -75,7 +88,7 @@ function y() {
|
|
|
75
88
|
C.length,
|
|
76
89
|
" active"
|
|
77
90
|
]
|
|
78
|
-
})] }), /* @__PURE__ */ u(r, { onCreated:
|
|
91
|
+
})] }), /* @__PURE__ */ u(r, { onCreated: D })]
|
|
79
92
|
}),
|
|
80
93
|
C.length > 0 && /* @__PURE__ */ d("div", {
|
|
81
94
|
className: "space-y-4",
|
|
@@ -93,7 +106,7 @@ function y() {
|
|
|
93
106
|
children: [/* @__PURE__ */ u(s, { className: "h-4 w-4 text-primary" }), /* @__PURE__ */ u("button", {
|
|
94
107
|
type: "button",
|
|
95
108
|
className: "hover:underline text-left",
|
|
96
|
-
onClick: () =>
|
|
109
|
+
onClick: () => D(e.id),
|
|
97
110
|
children: e.name
|
|
98
111
|
})]
|
|
99
112
|
}), /* @__PURE__ */ d("div", {
|
|
@@ -112,24 +125,17 @@ function y() {
|
|
|
112
125
|
className: "text-sm text-muted-foreground",
|
|
113
126
|
children: e.goal ?? ""
|
|
114
127
|
}),
|
|
115
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ u(a, { sprint: e }),
|
|
129
|
+
/* @__PURE__ */ u("div", {
|
|
116
130
|
className: "flex gap-4 text-xs text-muted-foreground",
|
|
117
|
-
children:
|
|
118
|
-
className: "flex items-center gap-1",
|
|
119
|
-
children: [
|
|
120
|
-
/* @__PURE__ */ u(a, { className: "h-3 w-3" }),
|
|
121
|
-
e.startDate ?? "?",
|
|
122
|
-
" - ",
|
|
123
|
-
e.endDate ?? "?"
|
|
124
|
-
]
|
|
125
|
-
}), /* @__PURE__ */ d("span", {
|
|
131
|
+
children: /* @__PURE__ */ d("span", {
|
|
126
132
|
className: "flex items-center gap-1",
|
|
127
133
|
children: [
|
|
128
134
|
/* @__PURE__ */ u(o, { className: "h-3 w-3" }),
|
|
129
135
|
t.teamMemberIds.length,
|
|
130
136
|
" members"
|
|
131
137
|
]
|
|
132
|
-
})
|
|
138
|
+
})
|
|
133
139
|
}),
|
|
134
140
|
/* @__PURE__ */ d("div", {
|
|
135
141
|
className: "grid grid-cols-3 gap-4 text-center",
|
|
@@ -241,7 +247,7 @@ function y() {
|
|
|
241
247
|
children: /* @__PURE__ */ u("button", {
|
|
242
248
|
type: "button",
|
|
243
249
|
className: "hover:underline text-left",
|
|
244
|
-
onClick: () =>
|
|
250
|
+
onClick: () => D(e.id),
|
|
245
251
|
children: e.name
|
|
246
252
|
})
|
|
247
253
|
}), /* @__PURE__ */ d("div", {
|
|
@@ -258,23 +264,61 @@ function y() {
|
|
|
258
264
|
})
|
|
259
265
|
}), /* @__PURE__ */ d(m, {
|
|
260
266
|
className: "space-y-2",
|
|
261
|
-
children: [
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
/* @__PURE__ */ d("span", { children: [t.totalPoints, " points planned"] }),
|
|
273
|
-
/* @__PURE__ */ d("span", { children: [t.teamMemberIds.length, " members"] })
|
|
274
|
-
]
|
|
275
|
-
})]
|
|
267
|
+
children: [
|
|
268
|
+
/* @__PURE__ */ u("p", {
|
|
269
|
+
className: "text-sm text-muted-foreground",
|
|
270
|
+
children: e.goal ?? ""
|
|
271
|
+
}),
|
|
272
|
+
/* @__PURE__ */ u(a, { sprint: e }),
|
|
273
|
+
/* @__PURE__ */ d("div", {
|
|
274
|
+
className: "flex gap-4 text-xs text-muted-foreground",
|
|
275
|
+
children: [/* @__PURE__ */ d("span", { children: [t.totalPoints, " points planned"] }), /* @__PURE__ */ d("span", { children: [t.teamMemberIds.length, " members"] })]
|
|
276
|
+
})
|
|
277
|
+
]
|
|
276
278
|
})] }, e.id);
|
|
277
279
|
})]
|
|
280
|
+
}),
|
|
281
|
+
T.length > 0 && /* @__PURE__ */ d("div", {
|
|
282
|
+
className: "space-y-4",
|
|
283
|
+
"data-testid": "finished-sprints",
|
|
284
|
+
children: [/* @__PURE__ */ u("h2", {
|
|
285
|
+
className: "text-lg font-medium",
|
|
286
|
+
children: "Finished"
|
|
287
|
+
}), T.map((e) => {
|
|
288
|
+
let t = v(e);
|
|
289
|
+
return /* @__PURE__ */ d(p, {
|
|
290
|
+
"data-testid": "finished-sprint",
|
|
291
|
+
children: [/* @__PURE__ */ u(h, {
|
|
292
|
+
className: "pb-2",
|
|
293
|
+
children: /* @__PURE__ */ d("div", {
|
|
294
|
+
className: "flex items-center justify-between",
|
|
295
|
+
children: [/* @__PURE__ */ u(g, {
|
|
296
|
+
className: "text-base",
|
|
297
|
+
children: /* @__PURE__ */ u("button", {
|
|
298
|
+
type: "button",
|
|
299
|
+
className: "hover:underline text-left",
|
|
300
|
+
onClick: () => D(e.id),
|
|
301
|
+
children: e.name
|
|
302
|
+
})
|
|
303
|
+
}), /* @__PURE__ */ u(f, {
|
|
304
|
+
variant: "outline",
|
|
305
|
+
children: e.status === "COMPLETED" ? "Completed" : "Cancelled"
|
|
306
|
+
})]
|
|
307
|
+
})
|
|
308
|
+
}), /* @__PURE__ */ d(m, {
|
|
309
|
+
className: "space-y-2",
|
|
310
|
+
children: [/* @__PURE__ */ u(a, { sprint: e }), /* @__PURE__ */ d("p", {
|
|
311
|
+
className: "text-xs text-muted-foreground",
|
|
312
|
+
children: [
|
|
313
|
+
t.completedPoints,
|
|
314
|
+
"/",
|
|
315
|
+
t.totalPoints,
|
|
316
|
+
" points done"
|
|
317
|
+
]
|
|
318
|
+
})]
|
|
319
|
+
})]
|
|
320
|
+
}, e.id);
|
|
321
|
+
})]
|
|
278
322
|
})
|
|
279
323
|
]
|
|
280
324
|
});
|