@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.
Files changed (92) hide show
  1. package/dist/generated/wspace-types.d.ts +61 -2
  2. package/dist/generated/wspace-types.d.ts.map +1 -1
  3. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoot.js +90 -79
  5. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  6. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  7. package/dist/planmagnet/PlanMagnetRoutes.js +124 -127
  8. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  9. package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
  10. package/dist/planmagnet/create/CreateDialogs.js +149 -124
  11. package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
  12. package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
  13. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +1 -1
  14. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  15. package/dist/planmagnet/pages/NotFoundPage.d.ts +6 -0
  16. package/dist/planmagnet/pages/NotFoundPage.d.ts.map +1 -0
  17. package/dist/planmagnet/pages/NotFoundPage.js +18 -0
  18. package/dist/planmagnet/pages/NotFoundPage.js.map +1 -0
  19. package/dist/planmagnet/pages/ProductsCreateProductPagePage.d.ts.map +1 -1
  20. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js +93 -98
  21. package/dist/planmagnet/pages/ProductsCreateProductPagePage.js.map +1 -1
  22. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js +72 -79
  24. package/dist/planmagnet/pages/ProductsCreateRoadmapPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  26. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +112 -123
  27. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  28. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
  29. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +103 -87
  30. package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  32. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +92 -98
  33. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  34. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  35. package/dist/planmagnet/pages/ProductsProductsPagePage.js +88 -96
  36. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
  38. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +116 -114
  39. package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
  40. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -88
  42. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +79 -79
  45. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js +111 -91
  48. package/dist/planmagnet/pages/ProjectsCreateProjectPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +117 -97
  51. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +150 -154
  54. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +154 -117
  57. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +75 -31
  60. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/TasksCreateTaskPagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +161 -115
  63. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js.map +1 -1
  64. package/dist/planmagnet/pages/TasksEditTaskPagePage.d.ts.map +1 -1
  65. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +117 -102
  66. package/dist/planmagnet/pages/TasksEditTaskPagePage.js.map +1 -1
  67. package/dist/planmagnet/pages/TeamInviteMemberPagePage.d.ts.map +1 -1
  68. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js +147 -147
  69. package/dist/planmagnet/pages/TeamInviteMemberPagePage.js.map +1 -1
  70. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  71. package/dist/planmagnet/pages/TeamTeamPagePage.js +77 -67
  72. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  73. package/dist/planmagnet/roadmaps/planningFocus.d.ts +23 -0
  74. package/dist/planmagnet/roadmaps/planningFocus.d.ts.map +1 -0
  75. package/dist/planmagnet/roadmaps/planningFocus.js +76 -0
  76. package/dist/planmagnet/roadmaps/planningFocus.js.map +1 -0
  77. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  78. package/dist/planmagnet/sprints/SprintControls.js +48 -29
  79. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  80. package/dist/planmagnet/sprints/SprintDates.d.ts +37 -0
  81. package/dist/planmagnet/sprints/SprintDates.d.ts.map +1 -0
  82. package/dist/planmagnet/sprints/SprintDates.js +114 -0
  83. package/dist/planmagnet/sprints/SprintDates.js.map +1 -0
  84. package/dist/planmagnet/utils/email.d.ts +9 -0
  85. package/dist/planmagnet/utils/email.d.ts.map +1 -0
  86. package/dist/planmagnet/utils/email.js +15 -0
  87. package/dist/planmagnet/utils/email.js.map +1 -0
  88. package/dist/planmagnet/utils/formValidation.d.ts +18 -0
  89. package/dist/planmagnet/utils/formValidation.d.ts.map +1 -0
  90. package/dist/planmagnet/utils/formValidation.js +17 -0
  91. package/dist/planmagnet/utils/formValidation.js.map +1 -0
  92. package/package.json +1 -1
@@ -1,14 +1,16 @@
1
1
  import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
- import { useEntityId as t } from "../hooks/useEntityId.js";
3
- import { SprintLifecycleActions as n } from "../sprints/SprintControls.js";
4
- import { useMemo as r, useState as i } from "react";
5
- import { ArrowLeft as a, Save as o } from "lucide-react";
6
- import { gql as s } from "@apollo/client";
7
- import { useMutation as c, useQuery as l } from "@apollo/client/react";
8
- import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
- import { Button as f, Card as p, CardContent as m, CardHeader as h, CardTitle as g, Input as _ } from "@burdenoff/fe-libs/ui";
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 v = s`
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
- `, y = s`
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 b() {
44
- let { workspaceId: s } = e(), b = t("sprintId"), { data: x, loading: S, error: C } = l(v, {
45
+ function C() {
46
+ let { workspaceId: u } = e(), C = i("sprintId"), { data: w, loading: T, error: E } = f(x, {
45
47
  variables: {
46
- id: b,
47
- workspaceId: s ?? "default"
48
+ id: C,
49
+ workspaceId: u ?? "default"
48
50
  },
49
- skip: !b
50
- }), [w, T] = i(null), E = x?.planmagnetSprint, D = x?.planmagnetProjects?.items ?? [], O = r(() => ({
51
- name: E?.name ?? "",
52
- startDate: E?.startDate ? E.startDate.split("T")[0] : "",
53
- endDate: E?.endDate ? E.endDate.split("T")[0] : "",
54
- goal: E?.goal ?? "",
55
- capacity: E?.capacity?.toString() ?? ""
56
- }), [E]), [k, A] = i({}), j = {
57
- ...O,
58
- ...k
59
- }, M = (e) => A(e), [N, { loading: P, error: F }] = c(y, {
60
- refetchQueries: [
61
- "GetSprints",
62
- "GetSprintDetail",
63
- "GetSprintForEdit",
64
- "GetDashboardData"
65
- ],
66
- onCompleted: () => {
67
- T("Sprint updated successfully.");
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__ */ u("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
73
- }) : C ? /* @__PURE__ */ d("div", {
74
- className: "p-6 text-red-500",
75
- children: ["Error loading sprint: ", C.message]
76
- }) : E ? /* @__PURE__ */ d("div", {
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__ */ d("div", {
80
+ /* @__PURE__ */ m("div", {
80
81
  className: "flex items-center justify-between",
81
- children: [/* @__PURE__ */ d("div", {
82
+ children: [/* @__PURE__ */ m("div", {
82
83
  className: "flex items-center gap-3",
83
- children: [/* @__PURE__ */ u(f, {
84
+ children: [/* @__PURE__ */ p(h, {
84
85
  variant: "ghost",
85
86
  size: "sm",
86
- children: /* @__PURE__ */ u(a, { className: "h-4 w-4" })
87
- }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("h1", {
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__ */ u("p", {
91
+ }), /* @__PURE__ */ p("p", {
91
92
  className: "text-sm text-muted-foreground",
92
- children: E.name
93
+ children: k.name
93
94
  })] })]
94
- }), /* @__PURE__ */ d(f, {
95
+ }), /* @__PURE__ */ m(h, {
95
96
  size: "sm",
96
- onClick: () => {
97
- E && N({ variables: {
98
- id: E.id,
99
- input: {
100
- name: j.name || void 0,
101
- startDate: j.startDate || void 0,
102
- endDate: j.endDate || void 0,
103
- goal: j.goal || void 0,
104
- capacity: j.capacity ? parseInt(j.capacity, 10) : void 0
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: P,
109
- children: [/* @__PURE__ */ u(o, { className: "mr-1 h-4 w-4" }), P ? "Saving..." : "Save Changes"]
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
- w && /* @__PURE__ */ u("div", {
113
- className: "p-3 bg-green-50 text-green-800 rounded-md text-sm",
114
- children: w
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
- F && /* @__PURE__ */ d("div", {
117
- className: "p-3 bg-red-50 text-red-800 rounded-md text-sm",
118
- children: ["Error: ", F.message]
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__ */ d(p, { children: [/* @__PURE__ */ u(h, { children: /* @__PURE__ */ u(g, { children: "Session Details" }) }), /* @__PURE__ */ d(m, {
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__ */ d("div", { children: [/* @__PURE__ */ u("label", {
135
+ /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
124
136
  className: "text-sm font-medium",
125
137
  children: "Name"
126
- }), /* @__PURE__ */ u(_, {
127
- value: j.name,
128
- onChange: (e) => M({
129
- ...j,
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__ */ d("div", {
146
+ /* @__PURE__ */ m("div", {
135
147
  className: "grid grid-cols-2 gap-4",
136
- children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
148
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
137
149
  className: "text-sm font-medium",
138
150
  children: "Project"
139
- }), /* @__PURE__ */ u("div", {
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: D.find((e) => e.id === E.projectId)?.name ?? E.projectId
142
- })] }), /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
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__ */ d("div", {
157
+ }), /* @__PURE__ */ m("div", {
146
158
  className: "mt-1 flex items-start justify-between gap-2",
147
- children: [/* @__PURE__ */ u("span", {
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: E.status.toLowerCase().replace(/_/g, " ")
150
- }), /* @__PURE__ */ u(n, {
151
- sprintId: E.id,
152
- sprintName: E.name,
153
- status: E.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__ */ d("div", {
169
+ /* @__PURE__ */ m("div", {
158
170
  className: "grid grid-cols-2 gap-4",
159
- children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
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__ */ u(_, {
175
+ }), /* @__PURE__ */ p(b, {
176
+ id: "sprint-start",
163
177
  type: "date",
164
- value: j.startDate,
165
- onChange: (e) => M({
166
- ...j,
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__ */ d("div", { children: [/* @__PURE__ */ u("label", {
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__ */ u(_, {
189
+ }), /* @__PURE__ */ p(b, {
190
+ id: "sprint-end",
174
191
  type: "date",
175
- value: j.endDate,
176
- onChange: (e) => M({
177
- ...j,
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
- /* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("label", {
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__ */ u("textarea", {
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: j.goal,
189
- onChange: (e) => M({
190
- ...j,
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__ */ d("div", { children: [/* @__PURE__ */ u("label", {
196
- className: "text-sm font-medium",
197
- children: "Capacity (story points)"
198
- }), /* @__PURE__ */ u(_, {
199
- type: "number",
200
- value: j.capacity,
201
- onChange: (e) => M({
202
- ...j,
203
- capacity: e.target.value
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
- className: "mt-1",
206
- placeholder: "e.g. 40"
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__ */ u("div", {
248
+ }) : /* @__PURE__ */ p("div", {
212
249
  className: "p-6 text-muted-foreground",
213
250
  children: "Planning session not found."
214
- }) : /* @__PURE__ */ u("div", {
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 { b as ProjectsPlanningEditPagePage };
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":"AAiFA,wBAAgB,0BAA0B,4CAwNzC"}
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 { Calendar as a, Users as o, Zap as s } from "lucide-react";
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 = t(), E = (e) => T(n("/projects/planning-detail-page", e));
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: E })]
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: () => E(e.id),
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__ */ d("div", {
128
+ /* @__PURE__ */ u(a, { sprint: e }),
129
+ /* @__PURE__ */ u("div", {
116
130
  className: "flex gap-4 text-xs text-muted-foreground",
117
- children: [/* @__PURE__ */ d("span", {
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: () => E(e.id),
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: [/* @__PURE__ */ u("p", {
262
- className: "text-sm text-muted-foreground",
263
- children: e.goal ?? ""
264
- }), /* @__PURE__ */ d("div", {
265
- className: "flex gap-4 text-xs text-muted-foreground",
266
- children: [
267
- /* @__PURE__ */ d("span", { children: [
268
- e.startDate ?? "?",
269
- " - ",
270
- e.endDate ?? "?"
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
  });