@burdenoff/microfe-planmagnet 2026.924.2 → 2026.924.3

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 (68) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +93 -142
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/admin/PlanMagnetAdminRoot.d.ts.map +1 -1
  5. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js +150 -149
  6. package/dist/planmagnet/admin/PlanMagnetAdminRoot.js.map +1 -1
  7. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  8. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +113 -116
  9. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  10. package/dist/planmagnet/pages/GoalsGoalsPagePage.d.ts.map +1 -1
  11. package/dist/planmagnet/pages/GoalsGoalsPagePage.js +59 -58
  12. package/dist/planmagnet/pages/GoalsGoalsPagePage.js.map +1 -1
  13. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.d.ts.map +1 -1
  14. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js +150 -149
  15. package/dist/planmagnet/pages/GoalsObjectiveDetailPagePage.js.map +1 -1
  16. package/dist/planmagnet/pages/ProductsEditProductPagePage.d.ts.map +1 -1
  17. package/dist/planmagnet/pages/ProductsEditProductPagePage.js +108 -107
  18. package/dist/planmagnet/pages/ProductsEditProductPagePage.js.map +1 -1
  19. package/dist/planmagnet/pages/ProductsProductDetailPagePage.d.ts.map +1 -1
  20. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js +93 -92
  21. package/dist/planmagnet/pages/ProductsProductDetailPagePage.js.map +1 -1
  22. package/dist/planmagnet/pages/ProductsProductsPagePage.d.ts.map +1 -1
  23. package/dist/planmagnet/pages/ProductsProductsPagePage.js +89 -87
  24. package/dist/planmagnet/pages/ProductsProductsPagePage.js.map +1 -1
  25. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.d.ts.map +1 -1
  26. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js +94 -93
  27. package/dist/planmagnet/pages/ProductsReleaseDetailPagePage.js.map +1 -1
  28. package/dist/planmagnet/pages/ProductsReleasesPagePage.d.ts.map +1 -1
  29. package/dist/planmagnet/pages/ProductsReleasesPagePage.js +57 -56
  30. package/dist/planmagnet/pages/ProductsReleasesPagePage.js.map +1 -1
  31. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
  32. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +82 -81
  33. package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
  34. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.d.ts.map +1 -1
  35. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js +72 -71
  36. package/dist/planmagnet/pages/ProductsRoadmapsPagePage.js.map +1 -1
  37. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.d.ts.map +1 -1
  38. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js +38 -37
  39. package/dist/planmagnet/pages/ProjectsActiveProjectsPagePage.js.map +1 -1
  40. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.d.ts.map +1 -1
  41. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +72 -71
  42. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js.map +1 -1
  43. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.d.ts.map +1 -1
  44. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js +47 -46
  45. package/dist/planmagnet/pages/ProjectsMilestonesPagePage.js.map +1 -1
  46. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  47. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +120 -119
  48. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  50. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +131 -130
  51. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  52. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  53. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +77 -75
  54. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  55. package/dist/planmagnet/pages/ReportsReportsPagePage.d.ts.map +1 -1
  56. package/dist/planmagnet/pages/ReportsReportsPagePage.js +71 -70
  57. package/dist/planmagnet/pages/ReportsReportsPagePage.js.map +1 -1
  58. package/dist/planmagnet/pages/TeamTeamPagePage.d.ts.map +1 -1
  59. package/dist/planmagnet/pages/TeamTeamPagePage.js +58 -57
  60. package/dist/planmagnet/pages/TeamTeamPagePage.js.map +1 -1
  61. package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.d.ts.map +1 -1
  62. package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js +81 -72
  63. package/dist/planmagnet/pages/WorkspaceCreateWorkspacePagePage.js.map +1 -1
  64. package/dist/planmagnet/people/People.d.ts +20 -0
  65. package/dist/planmagnet/people/People.d.ts.map +1 -0
  66. package/dist/planmagnet/people/People.js +42 -0
  67. package/dist/planmagnet/people/People.js.map +1 -0
  68. package/package.json +1 -1
@@ -1,11 +1,12 @@
1
- import { useEntityId as e } from "../hooks/useEntityId.js";
2
- import { BarChart3 as t, CalendarClock as n, FolderKanban as r, Users as i } from "lucide-react";
3
- import { gql as a } from "@apollo/client";
4
- import { useQuery as o } from "@apollo/client/react";
5
- import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
- import { Badge as l, Card as u, CardContent as d, CardHeader as f, CardTitle as p } from "@burdenoff/fe-libs/ui";
1
+ import { PersonName as e } from "../people/People.js";
2
+ import { useEntityId as t } from "../hooks/useEntityId.js";
3
+ import { BarChart3 as n, CalendarClock as r, FolderKanban as i, Users as a } from "lucide-react";
4
+ import { gql as o } from "@apollo/client";
5
+ import { useQuery as s } from "@apollo/client/react";
6
+ import { jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ import { Badge as u, Card as d, CardContent as f, CardHeader as p, CardTitle as m } from "@burdenoff/fe-libs/ui";
7
8
  //#region src/planmagnet/pages/ProjectsPlanningDetailPagePage.tsx
8
- var m = a`
9
+ var h = o`
9
10
  query GetSprintDetail($id: ID!) {
10
11
  planmagnetSprint(id: $id) {
11
12
  id
@@ -27,7 +28,7 @@ var m = a`
27
28
  }
28
29
  }
29
30
  }
30
- `, h = a`
31
+ `, g = o`
31
32
  query GetSprintCapacity($sprintId: ID!) {
32
33
  planmagnetSprintCapacity(sprintId: $sprintId) {
33
34
  sprintId
@@ -47,262 +48,262 @@ var m = a`
47
48
  }
48
49
  }
49
50
  `;
50
- function g() {
51
- let a = e("sprintId"), { data: g, loading: _, error: v } = o(m, {
52
- variables: { id: a },
53
- skip: !a
54
- }), { data: y } = o(h, {
55
- variables: { sprintId: a },
56
- skip: !a
51
+ function _() {
52
+ let o = t("sprintId"), { data: _, loading: v, error: y } = s(h, {
53
+ variables: { id: o },
54
+ skip: !o
55
+ }), { data: b } = s(g, {
56
+ variables: { sprintId: o },
57
+ skip: !o
57
58
  });
58
- if (!a) return /* @__PURE__ */ s("div", {
59
+ if (!o) return /* @__PURE__ */ c("div", {
59
60
  className: "p-6 text-muted-foreground",
60
61
  children: "No planning session selected. Open one from the list."
61
62
  });
62
- if (_) return /* @__PURE__ */ s("div", {
63
+ if (v) return /* @__PURE__ */ c("div", {
63
64
  className: "flex items-center justify-center p-12",
64
- children: /* @__PURE__ */ s("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
65
+ children: /* @__PURE__ */ c("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
65
66
  });
66
- if (v) return /* @__PURE__ */ c("div", {
67
+ if (y) return /* @__PURE__ */ l("div", {
67
68
  className: "p-6 text-red-500",
68
- children: ["Error loading planning session: ", v.message]
69
+ children: ["Error loading planning session: ", y.message]
69
70
  });
70
- let b = g?.planmagnetSprint;
71
- if (!b) return /* @__PURE__ */ s("div", {
71
+ let x = _?.planmagnetSprint;
72
+ if (!x) return /* @__PURE__ */ c("div", {
72
73
  className: "p-6 text-muted-foreground",
73
74
  children: "Planning session not found."
74
75
  });
75
- let x = y?.planmagnetSprintCapacity, S = {};
76
- for (let e of x?.members ?? []) S[e.actorId] = {
76
+ let S = b?.planmagnetSprintCapacity, C = {};
77
+ for (let e of S?.members ?? []) C[e.actorId] = {
77
78
  employeeName: e.employeeName,
78
79
  allocatedHours: e.allocatedHours,
79
80
  billableHours: e.billableHours,
80
81
  utilizationPercent: e.utilizationPercent
81
82
  };
82
- let C = b.workItems ?? [], w = C.reduce((e, t) => e + (t.storyPoints ?? 0), 0);
83
- C.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0);
84
- let T = b.capacity ?? w, E = T > 0 ? Math.round(w / T * 100) : 0, D = T - w, O = [...new Set(C.map((e) => e.assigneeId).filter(Boolean))];
85
- return /* @__PURE__ */ c("div", {
83
+ let w = x.workItems ?? [], T = w.reduce((e, t) => e + (t.storyPoints ?? 0), 0);
84
+ w.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0);
85
+ let E = x.capacity ?? T, D = E > 0 ? Math.round(T / E * 100) : 0, O = E - T, k = [...new Set(w.map((e) => e.assigneeId).filter(Boolean))];
86
+ return /* @__PURE__ */ l("div", {
86
87
  className: "space-y-6 p-6",
87
- children: [/* @__PURE__ */ c("div", {
88
+ children: [/* @__PURE__ */ l("div", {
88
89
  className: "flex items-center justify-between",
89
- children: [/* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("h1", {
90
+ children: [/* @__PURE__ */ l("div", { children: [/* @__PURE__ */ c("h1", {
90
91
  className: "text-2xl font-semibold",
91
- children: b.name
92
- }), /* @__PURE__ */ c("p", {
92
+ children: x.name
93
+ }), /* @__PURE__ */ l("p", {
93
94
  className: "text-sm text-muted-foreground",
94
95
  children: [
95
- b.projectId,
96
+ x.projectId,
96
97
  " ·",
97
98
  " ",
98
- b.startDate ? new Date(b.startDate).toLocaleDateString() : "—",
99
+ x.startDate ? new Date(x.startDate).toLocaleDateString() : "—",
99
100
  " -",
100
101
  " ",
101
- b.endDate ? new Date(b.endDate).toLocaleDateString() : "—"
102
+ x.endDate ? new Date(x.endDate).toLocaleDateString() : "—"
102
103
  ]
103
- })] }), /* @__PURE__ */ s("div", {
104
+ })] }), /* @__PURE__ */ c("div", {
104
105
  className: "flex gap-2",
105
- children: /* @__PURE__ */ s(l, {
106
+ children: /* @__PURE__ */ c(u, {
106
107
  className: "capitalize",
107
- children: b.status.toLowerCase().replace("_", " ")
108
+ children: x.status.toLowerCase().replace("_", " ")
108
109
  })
109
110
  })]
110
- }), /* @__PURE__ */ c("div", {
111
+ }), /* @__PURE__ */ l("div", {
111
112
  className: "grid grid-cols-1 lg:grid-cols-3 gap-6",
112
- children: [/* @__PURE__ */ c("div", {
113
+ children: [/* @__PURE__ */ l("div", {
113
114
  className: "lg:col-span-2 space-y-6",
114
- children: [/* @__PURE__ */ c(u, { children: [/* @__PURE__ */ s(f, { children: /* @__PURE__ */ s(p, { children: "Capacity Allocation" }) }), /* @__PURE__ */ c(d, {
115
+ children: [/* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ c(m, { children: "Capacity Allocation" }) }), /* @__PURE__ */ l(f, {
115
116
  className: "space-y-4",
116
- children: [/* @__PURE__ */ c("div", {
117
+ children: [/* @__PURE__ */ l("div", {
117
118
  className: "grid grid-cols-3 gap-4 text-center",
118
119
  children: [
119
- /* @__PURE__ */ c("div", {
120
+ /* @__PURE__ */ l("div", {
120
121
  className: "p-4 bg-muted rounded-md",
121
- children: [/* @__PURE__ */ s("p", {
122
+ children: [/* @__PURE__ */ c("p", {
122
123
  className: "text-2xl font-bold",
123
- children: T
124
- }), /* @__PURE__ */ s("p", {
124
+ children: E
125
+ }), /* @__PURE__ */ c("p", {
125
126
  className: "text-xs text-muted-foreground",
126
127
  children: "Capacity"
127
128
  })]
128
129
  }),
129
- /* @__PURE__ */ c("div", {
130
+ /* @__PURE__ */ l("div", {
130
131
  className: "p-4 bg-primary/10 rounded-md",
131
- children: [/* @__PURE__ */ s("p", {
132
+ children: [/* @__PURE__ */ c("p", {
132
133
  className: "text-2xl font-bold text-primary",
133
- children: w
134
- }), /* @__PURE__ */ s("p", {
134
+ children: T
135
+ }), /* @__PURE__ */ c("p", {
135
136
  className: "text-xs text-muted-foreground",
136
137
  children: "Allocated"
137
138
  })]
138
139
  }),
139
- /* @__PURE__ */ c("div", {
140
+ /* @__PURE__ */ l("div", {
140
141
  className: "p-4 bg-muted rounded-md",
141
- children: [/* @__PURE__ */ s("p", {
142
+ children: [/* @__PURE__ */ c("p", {
142
143
  className: "text-2xl font-bold",
143
- children: D > 0 ? D : 0
144
- }), /* @__PURE__ */ s("p", {
144
+ children: O > 0 ? O : 0
145
+ }), /* @__PURE__ */ c("p", {
145
146
  className: "text-xs text-muted-foreground",
146
147
  children: "Remaining"
147
148
  })]
148
149
  })
149
150
  ]
150
- }), /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ c("div", {
151
+ }), /* @__PURE__ */ l("div", { children: [/* @__PURE__ */ l("div", {
151
152
  className: "flex justify-between text-sm mb-2",
152
- children: [/* @__PURE__ */ s("span", {
153
+ children: [/* @__PURE__ */ c("span", {
153
154
  className: "text-muted-foreground",
154
155
  children: "Utilization"
155
- }), /* @__PURE__ */ c("span", {
156
+ }), /* @__PURE__ */ l("span", {
156
157
  className: "font-medium",
157
- children: [E, "%"]
158
+ children: [D, "%"]
158
159
  })]
159
- }), /* @__PURE__ */ s("div", {
160
+ }), /* @__PURE__ */ c("div", {
160
161
  className: "w-full h-4 rounded-full bg-muted overflow-hidden",
161
- children: /* @__PURE__ */ s("div", {
162
- className: `h-full rounded-full ${E > 90 ? "bg-red-400" : "bg-green-400"}`,
163
- style: { width: `${Math.min(E, 100)}%` }
162
+ children: /* @__PURE__ */ c("div", {
163
+ className: `h-full rounded-full ${D > 90 ? "bg-red-400" : "bg-green-400"}`,
164
+ style: { width: `${Math.min(D, 100)}%` }
164
165
  })
165
166
  })] })]
166
- })] }), /* @__PURE__ */ c(u, { children: [/* @__PURE__ */ s(f, { children: /* @__PURE__ */ c(p, { children: [
167
+ })] }), /* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ l(m, { children: [
167
168
  "Team Members (",
168
- O.length,
169
+ k.length,
169
170
  ")"
170
- ] }) }), /* @__PURE__ */ c(d, { children: [
171
- /* @__PURE__ */ c("div", {
171
+ ] }) }), /* @__PURE__ */ l(f, { children: [
172
+ /* @__PURE__ */ l("div", {
172
173
  className: "grid grid-cols-2 gap-3",
173
- children: [O.map((e) => {
174
- let t = S[e];
175
- return /* @__PURE__ */ c("div", {
174
+ children: [k.map((t) => {
175
+ let n = C[t];
176
+ return /* @__PURE__ */ l("div", {
176
177
  className: "flex items-center gap-2 border rounded-md p-2.5",
177
- children: [/* @__PURE__ */ s("div", {
178
+ children: [/* @__PURE__ */ c("div", {
178
179
  className: "h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary",
179
- children: e.slice(0, 2).toUpperCase()
180
- }), /* @__PURE__ */ c("div", {
180
+ children: t.slice(0, 2).toUpperCase()
181
+ }), /* @__PURE__ */ l("div", {
181
182
  className: "min-w-0",
182
- children: [/* @__PURE__ */ s("span", {
183
+ children: [/* @__PURE__ */ c("span", {
183
184
  className: "text-sm truncate block",
184
- children: t?.employeeName ?? e
185
- }), t && /* @__PURE__ */ c("span", {
185
+ children: n?.employeeName ?? /* @__PURE__ */ c(e, { id: t })
186
+ }), n && /* @__PURE__ */ l("span", {
186
187
  className: "text-xs text-muted-foreground",
187
188
  children: [
188
- t.allocatedHours,
189
+ n.allocatedHours,
189
190
  "h allocated (",
190
- t.billableHours,
191
+ n.billableHours,
191
192
  "h billable)",
192
- t.utilizationPercent != null && ` · ${t.utilizationPercent}% util`
193
+ n.utilizationPercent != null && ` · ${n.utilizationPercent}% util`
193
194
  ]
194
195
  })]
195
196
  })]
196
- }, e);
197
- }), O.length === 0 && /* @__PURE__ */ s("p", {
197
+ }, t);
198
+ }), k.length === 0 && /* @__PURE__ */ c("p", {
198
199
  className: "text-sm text-muted-foreground col-span-2 text-center py-4",
199
200
  children: "No team members assigned."
200
201
  })]
201
202
  }),
202
- x?.crewfoundryUnavailable && O.length > 0 && /* @__PURE__ */ s("p", {
203
+ S?.crewfoundryUnavailable && k.length > 0 && /* @__PURE__ */ c("p", {
203
204
  className: "text-xs text-muted-foreground mt-3 border-t pt-3",
204
205
  children: "Workforce allocation from CrewFoundry is unavailable for this workspace — no CrewFoundry module provisioned yet."
205
206
  }),
206
- !x?.crewfoundryUnavailable && (x?.unmatchedAssigneeIds?.length ?? 0) > 0 && /* @__PURE__ */ c("p", {
207
+ !S?.crewfoundryUnavailable && (S?.unmatchedAssigneeIds?.length ?? 0) > 0 && /* @__PURE__ */ l("p", {
207
208
  className: "text-xs text-muted-foreground mt-3 border-t pt-3",
208
- children: [x.unmatchedAssigneeIds.length, " assignee(s) have no matching CrewFoundry employee record — allocation hours shown only for matched employees."]
209
+ children: [S.unmatchedAssigneeIds.length, " assignee(s) have no matching CrewFoundry employee record — allocation hours shown only for matched employees."]
209
210
  })
210
211
  ] })] })]
211
- }), /* @__PURE__ */ c(u, { children: [/* @__PURE__ */ s(f, { children: /* @__PURE__ */ s(p, { children: "Session Info" }) }), /* @__PURE__ */ c(d, {
212
+ }), /* @__PURE__ */ l(d, { children: [/* @__PURE__ */ c(p, { children: /* @__PURE__ */ c(m, { children: "Session Info" }) }), /* @__PURE__ */ l(f, {
212
213
  className: "space-y-3 text-sm",
213
214
  children: [
214
- /* @__PURE__ */ c("div", {
215
+ /* @__PURE__ */ l("div", {
215
216
  className: "flex items-center gap-2",
216
217
  children: [
217
- /* @__PURE__ */ s(r, { className: "h-4 w-4 text-muted-foreground" }),
218
- /* @__PURE__ */ s("span", {
218
+ /* @__PURE__ */ c(i, { className: "h-4 w-4 text-muted-foreground" }),
219
+ /* @__PURE__ */ c("span", {
219
220
  className: "text-muted-foreground",
220
221
  children: "Project:"
221
222
  }),
222
- /* @__PURE__ */ s("span", {
223
+ /* @__PURE__ */ c("span", {
223
224
  className: "font-medium",
224
- children: b.projectId
225
+ children: x.projectId
225
226
  })
226
227
  ]
227
228
  }),
228
- /* @__PURE__ */ c("div", {
229
+ /* @__PURE__ */ l("div", {
229
230
  className: "flex items-center gap-2",
230
231
  children: [
231
- /* @__PURE__ */ s(n, { className: "h-4 w-4 text-muted-foreground" }),
232
- /* @__PURE__ */ s("span", {
232
+ /* @__PURE__ */ c(r, { className: "h-4 w-4 text-muted-foreground" }),
233
+ /* @__PURE__ */ c("span", {
233
234
  className: "text-muted-foreground",
234
235
  children: "Start:"
235
236
  }),
236
- /* @__PURE__ */ s("span", {
237
+ /* @__PURE__ */ c("span", {
237
238
  className: "font-medium",
238
- children: b.startDate ? new Date(b.startDate).toLocaleDateString() : "Not set"
239
+ children: x.startDate ? new Date(x.startDate).toLocaleDateString() : "Not set"
239
240
  })
240
241
  ]
241
242
  }),
242
- /* @__PURE__ */ c("div", {
243
+ /* @__PURE__ */ l("div", {
243
244
  className: "flex items-center gap-2",
244
245
  children: [
245
- /* @__PURE__ */ s(n, { className: "h-4 w-4 text-muted-foreground" }),
246
- /* @__PURE__ */ s("span", {
246
+ /* @__PURE__ */ c(r, { className: "h-4 w-4 text-muted-foreground" }),
247
+ /* @__PURE__ */ c("span", {
247
248
  className: "text-muted-foreground",
248
249
  children: "End:"
249
250
  }),
250
- /* @__PURE__ */ s("span", {
251
+ /* @__PURE__ */ c("span", {
251
252
  className: "font-medium",
252
- children: b.endDate ? new Date(b.endDate).toLocaleDateString() : "Not set"
253
+ children: x.endDate ? new Date(x.endDate).toLocaleDateString() : "Not set"
253
254
  })
254
255
  ]
255
256
  }),
256
- /* @__PURE__ */ c("div", {
257
+ /* @__PURE__ */ l("div", {
257
258
  className: "flex items-center gap-2",
258
259
  children: [
259
- /* @__PURE__ */ s(i, { className: "h-4 w-4 text-muted-foreground" }),
260
- /* @__PURE__ */ s("span", {
260
+ /* @__PURE__ */ c(a, { className: "h-4 w-4 text-muted-foreground" }),
261
+ /* @__PURE__ */ c("span", {
261
262
  className: "text-muted-foreground",
262
263
  children: "Assignees:"
263
264
  }),
264
- /* @__PURE__ */ s("span", {
265
+ /* @__PURE__ */ c("span", {
265
266
  className: "font-medium",
266
- children: O.length
267
+ children: k.length
267
268
  })
268
269
  ]
269
270
  }),
270
- /* @__PURE__ */ c("div", {
271
+ /* @__PURE__ */ l("div", {
271
272
  className: "flex items-center gap-2",
272
273
  children: [
273
- /* @__PURE__ */ s(t, { className: "h-4 w-4 text-muted-foreground" }),
274
- /* @__PURE__ */ s("span", {
274
+ /* @__PURE__ */ c(n, { className: "h-4 w-4 text-muted-foreground" }),
275
+ /* @__PURE__ */ c("span", {
275
276
  className: "text-muted-foreground",
276
277
  children: "Capacity:"
277
278
  }),
278
- /* @__PURE__ */ c("span", {
279
+ /* @__PURE__ */ l("span", {
279
280
  className: "font-medium",
280
- children: [T, " pts"]
281
+ children: [E, " pts"]
281
282
  })
282
283
  ]
283
284
  }),
284
- b.velocity != null && /* @__PURE__ */ c("div", {
285
+ x.velocity != null && /* @__PURE__ */ l("div", {
285
286
  className: "flex items-center gap-2",
286
287
  children: [
287
- /* @__PURE__ */ s(t, { className: "h-4 w-4 text-muted-foreground" }),
288
- /* @__PURE__ */ s("span", {
288
+ /* @__PURE__ */ c(n, { className: "h-4 w-4 text-muted-foreground" }),
289
+ /* @__PURE__ */ c("span", {
289
290
  className: "text-muted-foreground",
290
291
  children: "Velocity:"
291
292
  }),
292
- /* @__PURE__ */ c("span", {
293
+ /* @__PURE__ */ l("span", {
293
294
  className: "font-medium",
294
- children: [b.velocity, " pts"]
295
+ children: [x.velocity, " pts"]
295
296
  })
296
297
  ]
297
298
  }),
298
- b.goal && /* @__PURE__ */ c("div", {
299
+ x.goal && /* @__PURE__ */ l("div", {
299
300
  className: "border-t pt-3 mt-3",
300
- children: [/* @__PURE__ */ s("p", {
301
+ children: [/* @__PURE__ */ c("p", {
301
302
  className: "text-muted-foreground text-xs mb-1",
302
303
  children: "Goal"
303
- }), /* @__PURE__ */ s("p", {
304
+ }), /* @__PURE__ */ c("p", {
304
305
  className: "text-sm",
305
- children: b.goal
306
+ children: x.goal
306
307
  })]
307
308
  })
308
309
  ]
@@ -311,6 +312,6 @@ function g() {
311
312
  });
312
313
  }
313
314
  //#endregion
314
- export { g as ProjectsPlanningDetailPagePage };
315
+ export { _ as ProjectsPlanningDetailPagePage };
315
316
 
316
317
  //# sourceMappingURL=ProjectsPlanningDetailPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsPlanningDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.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 { CalendarClock, Users, BarChart3, FolderKanban } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n status\n startDate\n endDate\n goal\n capacity\n velocity\n workItems {\n id\n key\n status\n storyPoints\n assigneeId\n priority\n }\n }\n }\n`;\n\n// Real cross-layer CrewFoundry workforce capacity (BOFF-4444). Fails open on\n// the backend — an unreachable/unprovisioned CrewFoundry never blocks this\n// page, it just yields crewfoundryUnavailable: true, handled below.\nconst GET_SPRINT_CAPACITY = gql`\n query GetSprintCapacity($sprintId: ID!) {\n planmagnetSprintCapacity(sprintId: $sprintId) {\n sprintId\n crewfoundryUnavailable\n unmatchedAssigneeIds\n members {\n actorId\n employeeName\n department\n position\n allocatedHours\n billableHours\n utilizationPercent\n assignedStoryPoints\n assignedWorkItemCount\n }\n }\n }\n`;\n\nexport function ProjectsPlanningDetailPagePage() {\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_DETAIL, {\n variables: { id: sprintId },\n skip: !sprintId,\n });\n\n // Real cross-layer CrewFoundry capacity (BOFF-4444) — called unconditionally\n // (with `skip`) to keep hook order stable across the loading/error/loaded\n // renders above.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: capacityData } = useQuery<Record<string, any>>(GET_SPRINT_CAPACITY, {\n variables: { sprintId },\n skip: !sprintId,\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 if (loading)\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-red-500\">Error loading planning session: {error.message}</div>;\n\n const sprint = data?.planmagnetSprint;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n const capacity = capacityData?.planmagnetSprintCapacity;\n const allocationByAssignee: Record<\n string,\n {\n employeeName: string;\n allocatedHours: number;\n billableHours: number;\n utilizationPercent: number | null;\n }\n > = {};\n for (const member of capacity?.members ?? []) {\n allocationByAssignee[member.actorId] = {\n employeeName: member.employeeName,\n allocatedHours: member.allocatedHours,\n billableHours: member.billableHours,\n utilizationPercent: member.utilizationPercent,\n };\n }\n\n const workItems = sprint.workItems ?? [];\n const totalPoints = workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const completedPoints = workItems\n .filter((wi: { status: string }) => wi.status === 'DONE')\n .reduce((sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0), 0);\n const allocatedCapacity = sprint.capacity ?? totalPoints;\n const capPct = allocatedCapacity > 0 ? Math.round((totalPoints / allocatedCapacity) * 100) : 0;\n const remaining = allocatedCapacity - totalPoints;\n\n // Derive unique assignees from work items\n const uniqueAssignees = [\n ...new Set(\n workItems\n .map((wi: { assigneeId: string | null }) => wi.assigneeId)\n .filter(Boolean) as string[]\n ),\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.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprint.projectId} &middot;{' '}\n {sprint.startDate ? new Date(sprint.startDate).toLocaleDateString() : '—'} -{' '}\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : '—'}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n </div>\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Capacity Allocation</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{allocatedCapacity}</p>\n <p className=\"text-xs text-muted-foreground\">Capacity</p>\n </div>\n <div className=\"p-4 bg-primary/10 rounded-md\">\n <p className=\"text-2xl font-bold text-primary\">{totalPoints}</p>\n <p className=\"text-xs text-muted-foreground\">Allocated</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{remaining > 0 ? remaining : 0}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Utilization</span>\n <span className=\"font-medium\">{capPct}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-red-400' : 'bg-green-400'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Team Members ({uniqueAssignees.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-3\">\n {uniqueAssignees.map((assigneeId: string) => {\n const workforce = allocationByAssignee[assigneeId];\n return (\n <div\n key={assigneeId}\n className=\"flex items-center gap-2 border rounded-md p-2.5\"\n >\n <div className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\">\n {assigneeId.slice(0, 2).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <span className=\"text-sm truncate block\">\n {workforce?.employeeName ?? assigneeId}\n </span>\n {workforce && (\n <span className=\"text-xs text-muted-foreground\">\n {workforce.allocatedHours}h allocated ({workforce.billableHours}h\n billable)\n {workforce.utilizationPercent != null &&\n ` · ${workforce.utilizationPercent}% util`}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {uniqueAssignees.length === 0 && (\n <p className=\"text-sm text-muted-foreground col-span-2 text-center py-4\">\n No team members assigned.\n </p>\n )}\n </div>\n {/* Real cross-layer CrewFoundry workforce allocation (BOFF-4444). */}\n {capacity?.crewfoundryUnavailable && uniqueAssignees.length > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n Workforce allocation from CrewFoundry is unavailable for this workspace — no\n CrewFoundry module provisioned yet.\n </p>\n )}\n {!capacity?.crewfoundryUnavailable &&\n (capacity?.unmatchedAssigneeIds?.length ?? 0) > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n {capacity.unmatchedAssigneeIds.length} assignee(s) have no matching CrewFoundry\n employee record — allocation hours shown only for matched employees.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Session Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n <span className=\"font-medium\">{sprint.projectId}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Start:</span>\n <span className=\"font-medium\">\n {sprint.startDate ? new Date(sprint.startDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">End:</span>\n <span className=\"font-medium\">\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Assignees:</span>\n <span className=\"font-medium\">{uniqueAssignees.length}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Capacity:</span>\n <span className=\"font-medium\">{allocatedCapacity} pts</span>\n </div>\n {sprint.velocity != null && (\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Velocity:</span>\n <span className=\"font-medium\">{sprint.velocity} pts</span>\n </div>\n )}\n {sprint.goal && (\n <div className=\"border-t pt-3 mt-3\">\n <p className=\"text-muted-foreground text-xs mb-1\">Goal</p>\n <p className=\"text-sm\">{sprint.goal}</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsPlanningDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.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 { CalendarClock, Users, BarChart3, FolderKanban } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { PersonName } from '../people/People';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n status\n startDate\n endDate\n goal\n capacity\n velocity\n workItems {\n id\n key\n status\n storyPoints\n assigneeId\n priority\n }\n }\n }\n`;\n\n// Real cross-layer CrewFoundry workforce capacity (BOFF-4444). Fails open on\n// the backend — an unreachable/unprovisioned CrewFoundry never blocks this\n// page, it just yields crewfoundryUnavailable: true, handled below.\nconst GET_SPRINT_CAPACITY = gql`\n query GetSprintCapacity($sprintId: ID!) {\n planmagnetSprintCapacity(sprintId: $sprintId) {\n sprintId\n crewfoundryUnavailable\n unmatchedAssigneeIds\n members {\n actorId\n employeeName\n department\n position\n allocatedHours\n billableHours\n utilizationPercent\n assignedStoryPoints\n assignedWorkItemCount\n }\n }\n }\n`;\n\nexport function ProjectsPlanningDetailPagePage() {\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_DETAIL, {\n variables: { id: sprintId },\n skip: !sprintId,\n });\n\n // Real cross-layer CrewFoundry capacity (BOFF-4444) — called unconditionally\n // (with `skip`) to keep hook order stable across the loading/error/loaded\n // renders above.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const { data: capacityData } = useQuery<Record<string, any>>(GET_SPRINT_CAPACITY, {\n variables: { sprintId },\n skip: !sprintId,\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 if (loading)\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-red-500\">Error loading planning session: {error.message}</div>;\n\n const sprint = data?.planmagnetSprint;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n const capacity = capacityData?.planmagnetSprintCapacity;\n const allocationByAssignee: Record<\n string,\n {\n employeeName: string;\n allocatedHours: number;\n billableHours: number;\n utilizationPercent: number | null;\n }\n > = {};\n for (const member of capacity?.members ?? []) {\n allocationByAssignee[member.actorId] = {\n employeeName: member.employeeName,\n allocatedHours: member.allocatedHours,\n billableHours: member.billableHours,\n utilizationPercent: member.utilizationPercent,\n };\n }\n\n const workItems = sprint.workItems ?? [];\n const totalPoints = workItems.reduce(\n (sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0),\n 0\n );\n const completedPoints = workItems\n .filter((wi: { status: string }) => wi.status === 'DONE')\n .reduce((sum: number, wi: { storyPoints: number | null }) => sum + (wi.storyPoints ?? 0), 0);\n const allocatedCapacity = sprint.capacity ?? totalPoints;\n const capPct = allocatedCapacity > 0 ? Math.round((totalPoints / allocatedCapacity) * 100) : 0;\n const remaining = allocatedCapacity - totalPoints;\n\n // Derive unique assignees from work items\n const uniqueAssignees = [\n ...new Set(\n workItems\n .map((wi: { assigneeId: string | null }) => wi.assigneeId)\n .filter(Boolean) as string[]\n ),\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.name}</h1>\n <p className=\"text-sm text-muted-foreground\">\n {sprint.projectId} &middot;{' '}\n {sprint.startDate ? new Date(sprint.startDate).toLocaleDateString() : '—'} -{' '}\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : '—'}\n </p>\n </div>\n <div className=\"flex gap-2\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n </div>\n </div>\n <div className=\"grid grid-cols-1 lg:grid-cols-3 gap-6\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Capacity Allocation</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{allocatedCapacity}</p>\n <p className=\"text-xs text-muted-foreground\">Capacity</p>\n </div>\n <div className=\"p-4 bg-primary/10 rounded-md\">\n <p className=\"text-2xl font-bold text-primary\">{totalPoints}</p>\n <p className=\"text-xs text-muted-foreground\">Allocated</p>\n </div>\n <div className=\"p-4 bg-muted rounded-md\">\n <p className=\"text-2xl font-bold\">{remaining > 0 ? remaining : 0}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n <div>\n <div className=\"flex justify-between text-sm mb-2\">\n <span className=\"text-muted-foreground\">Utilization</span>\n <span className=\"font-medium\">{capPct}%</span>\n </div>\n <div className=\"w-full h-4 rounded-full bg-muted overflow-hidden\">\n <div\n className={`h-full rounded-full ${capPct > 90 ? 'bg-red-400' : 'bg-green-400'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Team Members ({uniqueAssignees.length})</CardTitle>\n </CardHeader>\n <CardContent>\n <div className=\"grid grid-cols-2 gap-3\">\n {uniqueAssignees.map((assigneeId: string) => {\n const workforce = allocationByAssignee[assigneeId];\n return (\n <div\n key={assigneeId}\n className=\"flex items-center gap-2 border rounded-md p-2.5\"\n >\n <div className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\">\n {assigneeId.slice(0, 2).toUpperCase()}\n </div>\n <div className=\"min-w-0\">\n <span className=\"text-sm truncate block\">\n {workforce?.employeeName ?? <PersonName id={assigneeId} />}\n </span>\n {workforce && (\n <span className=\"text-xs text-muted-foreground\">\n {workforce.allocatedHours}h allocated ({workforce.billableHours}h\n billable)\n {workforce.utilizationPercent != null &&\n ` · ${workforce.utilizationPercent}% util`}\n </span>\n )}\n </div>\n </div>\n );\n })}\n {uniqueAssignees.length === 0 && (\n <p className=\"text-sm text-muted-foreground col-span-2 text-center py-4\">\n No team members assigned.\n </p>\n )}\n </div>\n {/* Real cross-layer CrewFoundry workforce allocation (BOFF-4444). */}\n {capacity?.crewfoundryUnavailable && uniqueAssignees.length > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n Workforce allocation from CrewFoundry is unavailable for this workspace — no\n CrewFoundry module provisioned yet.\n </p>\n )}\n {!capacity?.crewfoundryUnavailable &&\n (capacity?.unmatchedAssigneeIds?.length ?? 0) > 0 && (\n <p className=\"text-xs text-muted-foreground mt-3 border-t pt-3\">\n {capacity.unmatchedAssigneeIds.length} assignee(s) have no matching CrewFoundry\n employee record — allocation hours shown only for matched employees.\n </p>\n )}\n </CardContent>\n </Card>\n </div>\n <Card>\n <CardHeader>\n <CardTitle>Session Info</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3 text-sm\">\n <div className=\"flex items-center gap-2\">\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Project:</span>\n <span className=\"font-medium\">{sprint.projectId}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Start:</span>\n <span className=\"font-medium\">\n {sprint.startDate ? new Date(sprint.startDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <CalendarClock className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">End:</span>\n <span className=\"font-medium\">\n {sprint.endDate ? new Date(sprint.endDate).toLocaleDateString() : 'Not set'}\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <Users className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Assignees:</span>\n <span className=\"font-medium\">{uniqueAssignees.length}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Capacity:</span>\n <span className=\"font-medium\">{allocatedCapacity} pts</span>\n </div>\n {sprint.velocity != null && (\n <div className=\"flex items-center gap-2\">\n <BarChart3 className=\"h-4 w-4 text-muted-foreground\" />\n <span className=\"text-muted-foreground\">Velocity:</span>\n <span className=\"font-medium\">{sprint.velocity} pts</span>\n </div>\n )}\n {sprint.goal && (\n <div className=\"border-t pt-3 mt-3\">\n <p className=\"text-muted-foreground text-xs mb-1\">Goal</p>\n <p className=\"text-sm\">{sprint.goal}</p>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"AAqEA,wBAAgB,6BAA6B,4CA8S5C"}
1
+ {"version":3,"file":"ProjectsProjectDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsProjectDetailPagePage.tsx"],"names":[],"mappings":"AAsEA,wBAAgB,6BAA6B,4CAkT5C"}