@burdenoff/microfe-planmagnet 2026.928.4 → 2026.928.6

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 (226) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +130 -97
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +256 -156
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/automation/AutomationExecutionsPanel.d.ts +5 -0
  8. package/dist/planmagnet/automation/AutomationExecutionsPanel.d.ts.map +1 -0
  9. package/dist/planmagnet/automation/AutomationExecutionsPanel.js +107 -0
  10. package/dist/planmagnet/automation/AutomationExecutionsPanel.js.map +1 -0
  11. package/dist/planmagnet/automation/AutomationRuleFields.d.ts +29 -0
  12. package/dist/planmagnet/automation/AutomationRuleFields.d.ts.map +1 -0
  13. package/dist/planmagnet/automation/AutomationRuleFields.js +308 -0
  14. package/dist/planmagnet/automation/AutomationRuleFields.js.map +1 -0
  15. package/dist/planmagnet/automation/automationRuleTypes.d.ts +60 -0
  16. package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -0
  17. package/dist/planmagnet/automation/automationRuleTypes.js +113 -0
  18. package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -0
  19. package/dist/planmagnet/board/BulkActions.d.ts +11 -0
  20. package/dist/planmagnet/board/BulkActions.d.ts.map +1 -0
  21. package/dist/planmagnet/board/BulkActions.js +182 -0
  22. package/dist/planmagnet/board/BulkActions.js.map +1 -0
  23. package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -1
  24. package/dist/planmagnet/board/ProjectBoard.js +289 -254
  25. package/dist/planmagnet/board/ProjectBoard.js.map +1 -1
  26. package/dist/planmagnet/budgets/BudgetControls.d.ts +30 -0
  27. package/dist/planmagnet/budgets/BudgetControls.d.ts.map +1 -0
  28. package/dist/planmagnet/budgets/BudgetControls.js +470 -0
  29. package/dist/planmagnet/budgets/BudgetControls.js.map +1 -0
  30. package/dist/planmagnet/budgets/ExpenseControls.d.ts +27 -0
  31. package/dist/planmagnet/budgets/ExpenseControls.d.ts.map +1 -0
  32. package/dist/planmagnet/budgets/ExpenseControls.js +366 -0
  33. package/dist/planmagnet/budgets/ExpenseControls.js.map +1 -0
  34. package/dist/planmagnet/budgets/ProjectBudgetsSummary.d.ts +5 -0
  35. package/dist/planmagnet/budgets/ProjectBudgetsSummary.d.ts.map +1 -0
  36. package/dist/planmagnet/budgets/ProjectBudgetsSummary.js +112 -0
  37. package/dist/planmagnet/budgets/ProjectBudgetsSummary.js.map +1 -0
  38. package/dist/planmagnet/budgets/budgetMath.d.ts +26 -0
  39. package/dist/planmagnet/budgets/budgetMath.d.ts.map +1 -0
  40. package/dist/planmagnet/budgets/budgetMath.js +49 -0
  41. package/dist/planmagnet/budgets/budgetMath.js.map +1 -0
  42. package/dist/planmagnet/controls/AutomationRuleControls.d.ts +23 -0
  43. package/dist/planmagnet/controls/AutomationRuleControls.d.ts.map +1 -0
  44. package/dist/planmagnet/controls/AutomationRuleControls.js +172 -0
  45. package/dist/planmagnet/controls/AutomationRuleControls.js.map +1 -0
  46. package/dist/planmagnet/controls/CompetitorControls.js +15 -15
  47. package/dist/planmagnet/controls/CustomFieldControls.d.ts +42 -0
  48. package/dist/planmagnet/controls/CustomFieldControls.d.ts.map +1 -0
  49. package/dist/planmagnet/controls/CustomFieldControls.js +84 -0
  50. package/dist/planmagnet/controls/CustomFieldControls.js.map +1 -0
  51. package/dist/planmagnet/controls/CustomFieldValuesPanel.d.ts +8 -0
  52. package/dist/planmagnet/controls/CustomFieldValuesPanel.d.ts.map +1 -0
  53. package/dist/planmagnet/controls/CustomFieldValuesPanel.js +315 -0
  54. package/dist/planmagnet/controls/CustomFieldValuesPanel.js.map +1 -0
  55. package/dist/planmagnet/controls/FeatureControls.js +54 -54
  56. package/dist/planmagnet/controls/ObjectiveControls.js +15 -15
  57. package/dist/planmagnet/controls/Pickers.d.ts +7 -0
  58. package/dist/planmagnet/controls/Pickers.d.ts.map +1 -1
  59. package/dist/planmagnet/controls/Pickers.js +58 -20
  60. package/dist/planmagnet/controls/Pickers.js.map +1 -1
  61. package/dist/planmagnet/controls/PortfolioControls.js +7 -7
  62. package/dist/planmagnet/controls/ProgramControls.js +19 -19
  63. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  64. package/dist/planmagnet/controls/ProjectControls.js +54 -47
  65. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  66. package/dist/planmagnet/controls/ReleaseControls.js +7 -7
  67. package/dist/planmagnet/controls/RiskControls.d.ts +27 -0
  68. package/dist/planmagnet/controls/RiskControls.d.ts.map +1 -0
  69. package/dist/planmagnet/controls/RiskControls.js +346 -0
  70. package/dist/planmagnet/controls/RiskControls.js.map +1 -0
  71. package/dist/planmagnet/controls/TemplateControls.d.ts +15 -0
  72. package/dist/planmagnet/controls/TemplateControls.d.ts.map +1 -0
  73. package/dist/planmagnet/controls/TemplateControls.js +55 -0
  74. package/dist/planmagnet/controls/TemplateControls.js.map +1 -0
  75. package/dist/planmagnet/controls/WorkItemControls.d.ts +6 -2
  76. package/dist/planmagnet/controls/WorkItemControls.d.ts.map +1 -1
  77. package/dist/planmagnet/controls/WorkItemControls.js +9 -9
  78. package/dist/planmagnet/controls/WorkItemControls.js.map +1 -1
  79. package/dist/planmagnet/create/CreateDialogs.d.ts +20 -0
  80. package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
  81. package/dist/planmagnet/create/CreateDialogs.js +455 -116
  82. package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
  83. package/dist/planmagnet/dependency/DependencyPanel.d.ts +6 -0
  84. package/dist/planmagnet/dependency/DependencyPanel.d.ts.map +1 -0
  85. package/dist/planmagnet/dependency/DependencyPanel.js +310 -0
  86. package/dist/planmagnet/dependency/DependencyPanel.js.map +1 -0
  87. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts +2 -0
  88. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts.map +1 -0
  89. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js +171 -0
  90. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js.map +1 -0
  91. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts +2 -0
  92. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts.map +1 -0
  93. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js +232 -0
  94. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js.map +1 -0
  95. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts +2 -0
  96. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts.map +1 -0
  97. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js +93 -0
  98. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js.map +1 -0
  99. package/dist/planmagnet/pages/DashboardDashboardPagePage.d.ts.map +1 -1
  100. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +11 -3
  101. package/dist/planmagnet/pages/DashboardDashboardPagePage.js.map +1 -1
  102. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.js +5 -5
  103. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +9 -9
  104. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.js +6 -6
  105. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.js +4 -4
  106. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.js +6 -6
  107. package/dist/planmagnet/pages/ProgramsProgramsPagePage.js +5 -5
  108. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts +2 -0
  109. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts.map +1 -0
  110. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js +315 -0
  111. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js.map +1 -0
  112. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts +2 -0
  113. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts.map +1 -0
  114. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +186 -0
  115. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js.map +1 -0
  116. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts +2 -0
  117. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts.map +1 -0
  118. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js +164 -0
  119. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js.map +1 -0
  120. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts +2 -0
  121. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts.map +1 -0
  122. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js +348 -0
  123. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js.map +1 -0
  124. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +10 -10
  125. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +4 -4
  126. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.d.ts.map +1 -1
  127. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +20 -10
  128. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js.map +1 -1
  129. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.d.ts.map +1 -1
  130. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +13 -8
  131. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js.map +1 -1
  132. package/dist/planmagnet/pages/ProjectsPlanningPagePage.d.ts.map +1 -1
  133. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +76 -56
  134. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js.map +1 -1
  135. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  136. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +372 -203
  137. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  138. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  139. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +81 -72
  140. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  141. package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts +2 -0
  142. package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts.map +1 -0
  143. package/dist/planmagnet/pages/RisksRiskDetailPagePage.js +278 -0
  144. package/dist/planmagnet/pages/RisksRiskDetailPagePage.js.map +1 -0
  145. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts +2 -0
  146. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts.map +1 -0
  147. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js +191 -0
  148. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js.map +1 -0
  149. package/dist/planmagnet/pages/SprintsStartSprintPagePage.d.ts.map +1 -1
  150. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +168 -122
  151. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js.map +1 -1
  152. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +5 -5
  153. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +4 -4
  154. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  155. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +161 -137
  156. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  157. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  158. package/dist/planmagnet/pages/TasksTasksPagePage.js +124 -113
  159. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  160. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts +2 -0
  161. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts.map +1 -0
  162. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +263 -0
  163. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js.map +1 -0
  164. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts +2 -0
  165. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts.map +1 -0
  166. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js +254 -0
  167. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js.map +1 -0
  168. package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts +2 -0
  169. package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts.map +1 -0
  170. package/dist/planmagnet/pages/TasksTimesheetsPagePage.js +165 -0
  171. package/dist/planmagnet/pages/TasksTimesheetsPagePage.js.map +1 -0
  172. package/dist/planmagnet/pages/TeamCreateTeamPagePage.d.ts.map +1 -1
  173. package/dist/planmagnet/pages/TeamCreateTeamPagePage.js +288 -91
  174. package/dist/planmagnet/pages/TeamCreateTeamPagePage.js.map +1 -1
  175. package/dist/planmagnet/pages/TeamTeamDetailPagePage.d.ts +2 -0
  176. package/dist/planmagnet/pages/TeamTeamDetailPagePage.d.ts.map +1 -0
  177. package/dist/planmagnet/pages/TeamTeamDetailPagePage.js +217 -0
  178. package/dist/planmagnet/pages/TeamTeamDetailPagePage.js.map +1 -0
  179. package/dist/planmagnet/pages/TeamTeamEditPagePage.d.ts +2 -0
  180. package/dist/planmagnet/pages/TeamTeamEditPagePage.d.ts.map +1 -0
  181. package/dist/planmagnet/pages/TeamTeamEditPagePage.js +211 -0
  182. package/dist/planmagnet/pages/TeamTeamEditPagePage.js.map +1 -0
  183. package/dist/planmagnet/pages/TeamTeamsListPagePage.d.ts +2 -0
  184. package/dist/planmagnet/pages/TeamTeamsListPagePage.d.ts.map +1 -0
  185. package/dist/planmagnet/pages/TeamTeamsListPagePage.js +118 -0
  186. package/dist/planmagnet/pages/TeamTeamsListPagePage.js.map +1 -0
  187. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts +2 -0
  188. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts.map +1 -0
  189. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js +316 -0
  190. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js.map +1 -0
  191. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts +2 -0
  192. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts.map +1 -0
  193. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js +208 -0
  194. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js.map +1 -0
  195. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts +0 -5
  196. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  197. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +47 -89
  198. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  199. package/dist/planmagnet/roadmaps/RoadmapControls.js +24 -24
  200. package/dist/planmagnet/sprints/SprintControls.d.ts +15 -3
  201. package/dist/planmagnet/sprints/SprintControls.d.ts.map +1 -1
  202. package/dist/planmagnet/sprints/SprintControls.js +293 -190
  203. package/dist/planmagnet/sprints/SprintControls.js.map +1 -1
  204. package/dist/planmagnet/team/TeamControls.d.ts +31 -0
  205. package/dist/planmagnet/team/TeamControls.d.ts.map +1 -0
  206. package/dist/planmagnet/team/TeamControls.js +407 -0
  207. package/dist/planmagnet/team/TeamControls.js.map +1 -0
  208. package/dist/planmagnet/time/TaskTimePanel.d.ts +7 -0
  209. package/dist/planmagnet/time/TaskTimePanel.d.ts.map +1 -0
  210. package/dist/planmagnet/time/TaskTimePanel.js +104 -0
  211. package/dist/planmagnet/time/TaskTimePanel.js.map +1 -0
  212. package/dist/planmagnet/time/TimeEntryControls.d.ts +37 -0
  213. package/dist/planmagnet/time/TimeEntryControls.d.ts.map +1 -0
  214. package/dist/planmagnet/time/TimeEntryControls.js +338 -0
  215. package/dist/planmagnet/time/TimeEntryControls.js.map +1 -0
  216. package/dist/planmagnet/time/TimesheetControls.d.ts +18 -0
  217. package/dist/planmagnet/time/TimesheetControls.d.ts.map +1 -0
  218. package/dist/planmagnet/time/TimesheetControls.js +244 -0
  219. package/dist/planmagnet/time/TimesheetControls.js.map +1 -0
  220. package/dist/planmagnet/time/timeShared.d.ts +29 -0
  221. package/dist/planmagnet/time/timeShared.d.ts.map +1 -0
  222. package/dist/planmagnet/time/timeShared.js +34 -0
  223. package/dist/planmagnet/time/timeShared.js.map +1 -0
  224. package/dist/planmagnet/utils/rowNavigation.js +5 -2
  225. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  226. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as t } from "../people/People.js";
3
- import { detailPath as n } from "../utils/rowNavigation.js";
4
- import { useEntityId as r } from "../hooks/useEntityId.js";
3
+ import { useEntityId as n } from "../hooks/useEntityId.js";
4
+ import { detailPath as r } from "../utils/rowNavigation.js";
5
5
  import { MilestoneHeaderActions as i } from "../controls/MilestoneControls.js";
6
6
  import { Calendar as a, FolderKanban as o, Target as s, User as c } from "lucide-react";
7
7
  import { gql as l } from "@apollo/client";
@@ -29,7 +29,7 @@ var v = l`
29
29
  }
30
30
  `;
31
31
  function y() {
32
- let l = r("milestoneId"), y = e(), { data: b, loading: x, error: S } = u(v, {
32
+ let l = n("milestoneId"), y = e(), { data: b, loading: x, error: S } = u(v, {
33
33
  variables: { id: l },
34
34
  skip: !l
35
35
  });
@@ -135,7 +135,7 @@ function y() {
135
135
  C.project ? /* @__PURE__ */ d("button", {
136
136
  type: "button",
137
137
  className: "font-medium text-primary hover:underline",
138
- onClick: () => y(n("/projects/project-detail-page", C.project.id)),
138
+ onClick: () => y(r("/projects/project-detail-page", C.project.id)),
139
139
  children: C.project.name
140
140
  }) : /* @__PURE__ */ d("span", {
141
141
  className: "text-muted-foreground",
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectsPlanningDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.tsx"],"names":[],"mappings":"AA8EA,wBAAgB,8BAA8B,4CA2R7C"}
1
+ {"version":3,"file":"ProjectsPlanningDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningDetailPagePage.tsx"],"names":[],"mappings":"AAuFA,wBAAgB,8BAA8B,4CAkT7C"}
@@ -1,7 +1,7 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonAvatar as t, PersonName as n, initialsOf as r } from "../people/People.js";
3
- import { detailPath as i } from "../utils/rowNavigation.js";
4
- import { useEntityId as a } from "../hooks/useEntityId.js";
3
+ import { useEntityId as i } from "../hooks/useEntityId.js";
4
+ import { detailPath as a } from "../utils/rowNavigation.js";
5
5
  import { SprintLifecycleActions as o, SprintWorkItemsPanel as s } from "../sprints/SprintControls.js";
6
6
  import { SprintDates as c } from "../sprints/SprintDates.js";
7
7
  import { BarChart3 as l, FolderKanban as u, Pencil as d, Users as f } from "lucide-react";
@@ -20,6 +20,15 @@ var C = p`
20
20
  id
21
21
  name
22
22
  }
23
+ teamId
24
+ team {
25
+ id
26
+ name
27
+ projects {
28
+ id
29
+ name
30
+ }
31
+ }
23
32
  status
24
33
  startDate
25
34
  endDate
@@ -70,7 +79,7 @@ var C = p`
70
79
  }
71
80
  `;
72
81
  function T() {
73
- let p = a("sprintId"), T = e(), { data: E, loading: D, error: O } = m(C, {
82
+ let p = i("sprintId"), T = e(), { data: E, loading: D, error: O } = m(C, {
74
83
  variables: { id: p },
75
84
  skip: !p
76
85
  }), { data: k } = m(w, {
@@ -103,7 +112,7 @@ function T() {
103
112
  };
104
113
  let N = A.workItems ?? [], P = N.reduce((e, t) => e + (t.storyPoints ?? 0), 0);
105
114
  N.filter((e) => e.status === "DONE").reduce((e, t) => e + (t.storyPoints ?? 0), 0);
106
- let F = A.capacity ?? null, I = F && F > 0 ? Math.round(P / F * 100) : null, L = F == null ? null : Math.max(0, F - P), R = [...new Set(N.map((e) => e.assigneeId).filter(Boolean))];
115
+ let F = A.capacity ?? null, I = F && F > 0 ? Math.round(P / F * 100) : null, L = F == null ? null : Math.max(0, F - P), R = [...new Set(N.map((e) => e.assigneeId).filter(Boolean))], z = A.team?.projects ?? [], B = A.team ? z.map((e) => e.id) : A.projectId ? [A.projectId] : [], V = A.team ? Object.fromEntries(z.map((e) => [e.id, e.name])) : void 0;
107
116
  return /* @__PURE__ */ g("div", {
108
117
  className: "space-y-6 p-6",
109
118
  children: [/* @__PURE__ */ g("div", {
@@ -115,7 +124,7 @@ function T() {
115
124
  }),
116
125
  /* @__PURE__ */ h("p", {
117
126
  className: "text-sm text-muted-foreground",
118
- children: A.project?.name ?? "Unknown project"
127
+ children: A.team ? `Team: ${A.team.name}` : A.project?.name ?? "Unknown project"
119
128
  }),
120
129
  /* @__PURE__ */ h("div", {
121
130
  className: "mt-1",
@@ -131,7 +140,7 @@ function T() {
131
140
  A.status !== "COMPLETED" && A.status !== "CANCELLED" && /* @__PURE__ */ g(v, {
132
141
  size: "sm",
133
142
  variant: "outline",
134
- onClick: () => T(i("/projects/planning-edit-page", A.id)),
143
+ onClick: () => T(a("/projects/planning-edit-page", A.id)),
135
144
  "data-testid": "sprint-edit",
136
145
  children: [/* @__PURE__ */ h(d, { className: "mr-1 h-4 w-4" }), " Edit"]
137
146
  }),
@@ -205,7 +214,8 @@ function T() {
205
214
  })] }),
206
215
  /* @__PURE__ */ g(y, { children: [/* @__PURE__ */ h(x, { children: /* @__PURE__ */ h(S, { children: "Work Items" }) }), /* @__PURE__ */ h(b, { children: /* @__PURE__ */ h(s, {
207
216
  sprintId: A.id,
208
- projectId: A.projectId,
217
+ eligibleProjectIds: B,
218
+ projectNames: V,
209
219
  status: A.status,
210
220
  items: N
211
221
  }) })] }),
@@ -265,14 +275,14 @@ function T() {
265
275
  /* @__PURE__ */ g("div", {
266
276
  className: "flex items-center gap-2",
267
277
  children: [
268
- /* @__PURE__ */ h(u, { className: "h-4 w-4 text-muted-foreground" }),
278
+ A.team ? /* @__PURE__ */ h(f, { className: "h-4 w-4 text-muted-foreground" }) : /* @__PURE__ */ h(u, { className: "h-4 w-4 text-muted-foreground" }),
269
279
  /* @__PURE__ */ h("span", {
270
280
  className: "text-muted-foreground",
271
- children: "Project:"
281
+ children: A.team ? "Team:" : "Project:"
272
282
  }),
273
283
  /* @__PURE__ */ h("span", {
274
284
  className: "font-medium",
275
- children: A.project?.name ?? "Unknown project"
285
+ children: A.team ? A.team.name : A.project?.name ?? "Unknown project"
276
286
  })
277
287
  ]
278
288
  }),
@@ -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, Button } from '@burdenoff/fe-libs/ui';\nimport { Users, BarChart3, FolderKanban, Pencil } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonAvatar, PersonName, initialsOf } from '../people/People';\nimport { SprintLifecycleActions, SprintWorkItemsPanel } from '../sprints/SprintControls';\nimport { SprintDates } from '../sprints/SprintDates';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n project {\n id\n name\n }\n status\n startDate\n endDate\n startedAt\n completedAt\n baselineStartDate\n baselineEndDate\n planChanges {\n field\n fromValue\n toValue\n sprintStatus\n changedBy\n changedAt\n }\n goal\n capacity\n velocity\n workItems {\n id\n key\n title\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 const navigate = usePlanMagnetNavigate();\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_SPRINT_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 // 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-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 // No capacity set means \"not planned\", not \"full\": this used to take the allocated points as the capacity and show\n // 100% utilisation in red, while the list showed no capacity for the same sprint (BOFF-7974).\n const sprintCapacity: number | null = sprint.capacity ?? null;\n const capPct =\n sprintCapacity && sprintCapacity > 0 ? Math.round((totalPoints / sprintCapacity) * 100) : null;\n const remaining = sprintCapacity == null ? null : Math.max(0, sprintCapacity - 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.project?.name ?? 'Unknown project'}\n </p>\n <div className=\"mt-1\">\n <SprintDates sprint={sprint} />\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n {/* The edit page existed but nothing linked to it, so a sprint's\n name, planned dates, goal and capacity could not be changed from\n the UI (BOFF-7954). Finished sprints are not edited here. */}\n {sprint.status !== 'COMPLETED' && sprint.status !== 'CANCELLED' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/projects/planning-edit-page', sprint.id))}\n data-testid=\"sprint-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\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\">{sprintCapacity ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">\n {sprintCapacity == null ? 'No capacity set' : 'Capacity'}\n </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 ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n {capPct !== null ? (\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-destructive' : 'bg-success'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Set a capacity on the sprint to see its utilisation.\n </p>\n )}\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Work Items</CardTitle>\n </CardHeader>\n <CardContent>\n <SprintWorkItemsPanel\n sprintId={sprint.id}\n projectId={sprint.projectId}\n status={sprint.status}\n items={workItems}\n />\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 {/* Initials from the name shown next to them, not from the UUID (BOFF-8000). */}\n {workforce?.employeeName ? (\n <span\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n title={workforce.employeeName}\n >\n {initialsOf(workforce.employeeName) || '?'}\n </span>\n ) : (\n <PersonAvatar\n id={assigneeId}\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n />\n )}\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.project?.name ?? 'Unknown project'}</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\">\n {sprintCapacity == null ? 'not set' : `${sprintCapacity} pts`}\n </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":";;;;;;;;;;;;AAaA,IAAM,IAAoB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CvB,IAAsB,CAAG;;;;;;;;;;;;;;;;;;;;AAqB/B,SAAgB,IAAiC;CAC/C,IAAM,IAAW,EAAY,WAAW,EAClC,IAAW,GAAuB,EAElC,EACJ,SACA,YACA,aAGA,EAA8B,GAAmB;EAC/C,WAAW,EAAE,IAAI,GAAU;EAC3B,MAAM,CAAC;EACR,CAAC,EAME,EAAE,MAAM,MAAiB,EAA8B,GAAqB;EAChF,WAAW,EAAE,aAAU;EACvB,MAAM,CAAC;EACR,CAAC;AAEF,KAAI,CAAC,EACH,QACE,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAErC,CAAA;AAIV,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD,EAAK,WAAU,kFAAmF,CAAA;EAC9F,CAAA;AAEV,KAAI,EACF,QAAO,kBAAC,OAAD;EAAK,WAAU;YAAf,CAAkC,oCAAiC,EAAM,QAAc;;CAEhG,IAAM,IAAS,GAAM;AACrB,KAAI,CAAC,EAAQ,QAAO,kBAAC,OAAD;EAAK,WAAU;YAA4B;EAAiC,CAAA;CAEhG,IAAM,IAAW,GAAc,0BACzB,IAQF,EAAE;AACN,MAAK,IAAM,KAAU,GAAU,WAAW,EAAE,CAC1C,GAAqB,EAAO,WAAW;EACrC,cAAc,EAAO;EACrB,gBAAgB,EAAO;EACvB,eAAe,EAAO;EACtB,oBAAoB,EAAO;EAC5B;CAGH,IAAM,IAAY,EAAO,aAAa,EAAE,EAClC,IAAc,EAAU,QAC3B,GAAa,MAAuC,KAAO,EAAG,eAAe,IAC9E,EACD;AACuB,GACrB,QAAQ,MAA2B,EAAG,WAAW,OAAO,CACxD,QAAQ,GAAa,MAAuC,KAAO,EAAG,eAAe,IAAI,EAAE;CAG9F,IAAM,IAAgC,EAAO,YAAY,MACnD,IACJ,KAAkB,IAAiB,IAAI,KAAK,MAAO,IAAc,IAAkB,IAAI,GAAG,MACtF,IAAY,KAAkB,OAAO,OAAO,KAAK,IAAI,GAAG,IAAiB,EAAY,EAGrF,IAAkB,CACtB,GAAG,IAAI,IACL,EACG,KAAK,MAAsC,EAAG,WAAW,CACzD,OAAO,QAAQ,CACnB,CACF;AAED,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA;IACE,kBAAC,MAAD;KAAI,WAAU;eAA0B,EAAO;KAAU,CAAA;IACzD,kBAAC,KAAD;KAAG,WAAU;eACV,EAAO,SAAS,QAAQ;KACvB,CAAA;IACJ,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD,EAAqB,WAAU,CAAA;KAC3B,CAAA;IACF,EAAA,CAAA,EACN,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAO,WAAU;gBAAc,EAAO,OAAO,aAAa,CAAC,QAAQ,KAAK,IAAI;MAAS,CAAA;KAIpF,EAAO,WAAW,eAAe,EAAO,WAAW,eAClD,kBAAC,GAAD;MACE,MAAK;MACL,SAAQ;MACR,eAAe,EAAS,EAAW,gCAAgC,EAAO,GAAG,CAAC;MAC9E,eAAY;gBAJd,CAME,kBAAC,GAAD,EAAQ,WAAU,gBAAiB,CAAA,EAAA,QAC5B;;KAEX,kBAAC,GAAD;MACE,UAAU,EAAO;MACjB,YAAY,EAAO;MACnB,QAAQ,EAAO;MACf,CAAA;KACE;MACF;MACN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,uBAA+B,CAAA,EAC/B,CAAA,EACb,kBAAC,GAAD;MAAa,WAAU;gBAAvB,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAsB,KAAkB;UAAQ,CAAA,EAC7D,kBAAC,KAAD;UAAG,WAAU;oBACV,KAAkB,OAAO,oBAAoB;UAC5C,CAAA,CACA;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAmC;UAAgB,CAAA,EAChE,kBAAC,KAAD;UAAG,WAAU;oBAAgC;UAAa,CAAA,CACtD;;QACN,kBAAC,OAAD;SAAK,WAAU;mBAAf,CACE,kBAAC,KAAD;UAAG,WAAU;oBAAsB,KAAa;UAAQ,CAAA,EACxD,kBAAC,KAAD;UAAG,WAAU;oBAAgC;UAAa,CAAA,CACtD;;QACF;UACL,MAAW,OAcV,kBAAC,KAAD;OAAG,WAAU;iBAAgC;OAEzC,CAAA,GAfJ,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAkB,CAAA,EAC1D,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+B,GAAO,IAAQ;UAC1C;UACN,kBAAC,OAAD;OAAK,WAAU;iBACb,kBAAC,OAAD;QACE,WAAW,uBAAuB,IAAS,KAAK,mBAAmB;QACnE,OAAO,EAAE,OAAO,GAAG,KAAK,IAAI,GAAQ,IAAI,CAAC,IAAI;QAC7C,CAAA;OACE,CAAA,CACF,EAAA,CAAA,CAMI;QACT,EAAA,CAAA;KACP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,cAAsB,CAAA,EACtB,CAAA,EACb,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;MACE,UAAU,EAAO;MACjB,WAAW,EAAO;MAClB,QAAQ,EAAO;MACf,OAAO;MACP,CAAA,EACU,CAAA,CACT,EAAA,CAAA;KACP,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAA;MAAW;MAAe,EAAgB;MAAO;MAAa,EAAA,CAAA,EACnD,CAAA,EACb,kBAAC,GAAD,EAAA,UAAA;MACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACG,EAAgB,KAAK,MAAuB;QAC3C,IAAM,IAAY,EAAqB;AACvC,eACE,kBAAC,OAAD;SAEE,WAAU;mBAFZ,CAKG,GAAW,eACV,kBAAC,QAAD;UACE,WAAU;UACV,OAAO,EAAU;oBAEhB,EAAW,EAAU,aAAa,IAAI;UAClC,CAAA,GAEP,kBAAC,GAAD;UACE,IAAI;UACJ,WAAU;UACV,CAAA,EAEJ,kBAAC,OAAD;UAAK,WAAU;oBAAf,CACE,kBAAC,QAAD;WAAM,WAAU;qBACb,GAAW,gBAAgB,kBAAC,GAAD,EAAY,IAAI,GAAc,CAAA;WACrD,CAAA,EACN,KACC,kBAAC,QAAD;WAAM,WAAU;qBAAhB;YACG,EAAU;YAAe;YAAc,EAAU;YAAc;YAE/D,EAAU,sBAAsB,QAC/B,MAAM,EAAU,mBAAmB;YAChC;aAEL;YACF;WA9BC,EA8BD;SAER,EACD,EAAgB,WAAW,KAC1B,kBAAC,KAAD;QAAG,WAAU;kBAA4D;QAErE,CAAA,CAEF;;MAEL,GAAU,0BAA0B,EAAgB,SAAS,KAC5D,kBAAC,KAAD;OAAG,WAAU;iBAAmD;OAG5D,CAAA;MAEL,CAAC,GAAU,2BACT,GAAU,sBAAsB,UAAU,KAAK,KAC9C,kBAAC,KAAD;OAAG,WAAU;iBAAb,CACG,EAAS,qBAAqB,QAAO,iHAEpC;;MAEI,EAAA,CAAA,CACT,EAAA,CAAA;KACH;OACN,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD,EAAA,UAAW,gBAAwB,CAAA,EACxB,CAAA,EACb,kBAAC,GAAD;IAAa,WAAU;cAAvB;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAc,WAAU,iCAAkC,CAAA;OAC1D,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAe,CAAA;OACvD,kBAAC,QAAD;QAAM,WAAU;kBAAe,EAAO,SAAS,QAAQ;QAAyB,CAAA;OAC5E;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAO,WAAU,iCAAkC,CAAA;OACnD,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAiB,CAAA;OACzD,kBAAC,QAAD;QAAM,WAAU;kBAAe,EAAgB;QAAc,CAAA;OACzD;;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAW,WAAU,iCAAkC,CAAA;OACvD,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAgB,CAAA;OACxD,kBAAC,QAAD;QAAM,WAAU;kBACb,KAAkB,OAAO,YAAY,GAAG,EAAe;QACnD,CAAA;OACH;;KACL,EAAO,YAAY,QAClB,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,GAAD,EAAW,WAAU,iCAAkC,CAAA;OACvD,kBAAC,QAAD;QAAM,WAAU;kBAAwB;QAAgB,CAAA;OACxD,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+B,EAAO,UAAS,OAAW;;OACtD;;KAEP,EAAO,QACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBAAqC;OAAQ,CAAA,EAC1D,kBAAC,KAAD;OAAG,WAAU;iBAAW,EAAO;OAAS,CAAA,CACpC;;KAEI;MACT,EAAA,CAAA,CACH;KACF"}
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, Button } from '@burdenoff/fe-libs/ui';\nimport { Users, BarChart3, FolderKanban, Pencil } from 'lucide-react';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { PersonAvatar, PersonName, initialsOf } from '../people/People';\nimport { SprintLifecycleActions, SprintWorkItemsPanel } from '../sprints/SprintControls';\nimport { SprintDates } from '../sprints/SprintDates';\n\nconst GET_SPRINT_DETAIL = gql`\n query GetSprintDetail($id: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n project {\n id\n name\n }\n teamId\n team {\n id\n name\n projects {\n id\n name\n }\n }\n status\n startDate\n endDate\n startedAt\n completedAt\n baselineStartDate\n baselineEndDate\n planChanges {\n field\n fromValue\n toValue\n sprintStatus\n changedBy\n changedAt\n }\n goal\n capacity\n velocity\n workItems {\n id\n key\n title\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 const navigate = usePlanMagnetNavigate();\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_SPRINT_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 // 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-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 // No capacity set means \"not planned\", not \"full\": this used to take the allocated points as the capacity and show\n // 100% utilisation in red, while the list showed no capacity for the same sprint (BOFF-7974).\n const sprintCapacity: number | null = sprint.capacity ?? null;\n const capPct =\n sprintCapacity && sprintCapacity > 0 ? Math.round((totalPoints / sprintCapacity) * 100) : null;\n const remaining = sprintCapacity == null ? null : Math.max(0, sprintCapacity - 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 // A sprint's scope is PROJECT xor TEAM (BOFF-8044). A team sprint has no `project` — showing\n // \"Unknown project\" for it (BOFF-8044 review) was wrong on the one page a user lands on right\n // after creating a team sprint. Its eligible work items are the team's own live member\n // projects, never the whole workspace (see SprintWorkItemsPanel/AddWorkItemsDialog).\n const teamProjects: { id: string; name: string }[] = sprint.team?.projects ?? [];\n const eligibleProjectIds = sprint.team\n ? teamProjects.map((p) => p.id)\n : sprint.projectId\n ? [sprint.projectId]\n : [];\n const projectNames = sprint.team\n ? Object.fromEntries(teamProjects.map((p) => [p.id, p.name]))\n : undefined;\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.team\n ? `Team: ${sprint.team.name}`\n : (sprint.project?.name ?? 'Unknown project')}\n </p>\n <div className=\"mt-1\">\n <SprintDates sprint={sprint} />\n </div>\n </div>\n <div className=\"flex items-start gap-3\">\n <Badge className=\"capitalize\">{sprint.status.toLowerCase().replace('_', ' ')}</Badge>\n {/* The edit page existed but nothing linked to it, so a sprint's\n name, planned dates, goal and capacity could not be changed from\n the UI (BOFF-7954). Finished sprints are not edited here. */}\n {sprint.status !== 'COMPLETED' && sprint.status !== 'CANCELLED' && (\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => navigate(detailPath('/projects/planning-edit-page', sprint.id))}\n data-testid=\"sprint-edit\"\n >\n <Pencil className=\"mr-1 h-4 w-4\" /> Edit\n </Button>\n )}\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\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\">{sprintCapacity ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">\n {sprintCapacity == null ? 'No capacity set' : 'Capacity'}\n </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 ?? '—'}</p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n {capPct !== null ? (\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-destructive' : 'bg-success'}`}\n style={{ width: `${Math.min(capPct, 100)}%` }}\n />\n </div>\n </div>\n ) : (\n <p className=\"text-sm text-muted-foreground\">\n Set a capacity on the sprint to see its utilisation.\n </p>\n )}\n </CardContent>\n </Card>\n <Card>\n <CardHeader>\n <CardTitle>Work Items</CardTitle>\n </CardHeader>\n <CardContent>\n <SprintWorkItemsPanel\n sprintId={sprint.id}\n eligibleProjectIds={eligibleProjectIds}\n projectNames={projectNames}\n status={sprint.status}\n items={workItems}\n />\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 {/* Initials from the name shown next to them, not from the UUID (BOFF-8000). */}\n {workforce?.employeeName ? (\n <span\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n title={workforce.employeeName}\n >\n {initialsOf(workforce.employeeName) || '?'}\n </span>\n ) : (\n <PersonAvatar\n id={assigneeId}\n className=\"h-8 w-8 rounded-full bg-primary/10 flex items-center justify-center text-xs font-medium text-primary\"\n />\n )}\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 {sprint.team ? (\n <Users className=\"h-4 w-4 text-muted-foreground\" />\n ) : (\n <FolderKanban className=\"h-4 w-4 text-muted-foreground\" />\n )}\n <span className=\"text-muted-foreground\">{sprint.team ? 'Team:' : 'Project:'}</span>\n <span className=\"font-medium\">\n {sprint.team ? sprint.team.name : (sprint.project?.name ?? 'Unknown project')}\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\">\n {sprintCapacity == null ? 'not set' : `${sprintCapacity} pts`}\n </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":"ProjectsPlanningEditPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx"],"names":[],"mappings":"AAiDA,wBAAgB,4BAA4B,4CA8P3C"}
1
+ {"version":3,"file":"ProjectsPlanningEditPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx"],"names":[],"mappings":"AAsDA,wBAAgB,4BAA4B,4CAmQ3C"}
@@ -1,8 +1,8 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { throwOnError as n } from "../utils/mutationResult.js";
3
- import { detailPath as r } from "../utils/rowNavigation.js";
4
- import { endBeforeStart as i, parseIntField as a } from "../utils/formValidation.js";
5
- import { useEntityId as o } from "../hooks/useEntityId.js";
3
+ import { useEntityId as r } from "../hooks/useEntityId.js";
4
+ import { detailPath as i } from "../utils/rowNavigation.js";
5
+ import { endBeforeStart as a, parseIntField as o } from "../utils/formValidation.js";
6
6
  import { SprintLifecycleActions as s } from "../sprints/SprintControls.js";
7
7
  import { useMemo as c, useState as l } from "react";
8
8
  import { ArrowLeft as u, Save as d } from "lucide-react";
@@ -17,6 +17,11 @@ var C = f`
17
17
  id
18
18
  name
19
19
  projectId
20
+ teamId
21
+ team {
22
+ id
23
+ name
24
+ }
20
25
  status
21
26
  startDate
22
27
  endDate
@@ -44,7 +49,7 @@ var C = f`
44
49
  }
45
50
  `;
46
51
  function T() {
47
- let { workspaceId: f } = e(), T = o("sprintId"), E = t(), { data: D, loading: O, error: k } = m(C, {
52
+ let { workspaceId: f } = e(), T = r("sprintId"), E = t(), { data: D, loading: O, error: k } = m(C, {
48
53
  variables: {
49
54
  id: T,
50
55
  workspaceId: f ?? "default"
@@ -63,7 +68,7 @@ function T() {
63
68
  let n = { ...t };
64
69
  for (let [t, r] of Object.entries(e)) r !== L[t] && (n[t] = r);
65
70
  return n;
66
- }), [z, B] = l(null), V = i(L.startDate, L.endDate), H = a(L.capacity, { min: 0 }), [U, { loading: W }] = p(w, { refetchQueries: [
71
+ }), [z, B] = l(null), V = a(L.startDate, L.endDate), H = o(L.capacity, { min: 0 }), [U, { loading: W }] = p(w, { refetchQueries: [
67
72
  "GetSprints",
68
73
  "GetSprintDetail",
69
74
  "GetSprintForEdit",
@@ -86,7 +91,7 @@ function T() {
86
91
  variant: "ghost",
87
92
  size: "sm",
88
93
  "aria-label": "Back to the sprint",
89
- onClick: () => E(r("/projects/planning-detail-page", T)),
94
+ onClick: () => E(i("/projects/planning-detail-page", T)),
90
95
  children: /* @__PURE__ */ h(u, { className: "h-4 w-4" })
91
96
  }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
92
97
  className: "text-2xl font-semibold",
@@ -150,10 +155,10 @@ function T() {
150
155
  className: "grid grid-cols-2 gap-4",
151
156
  children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
152
157
  className: "text-sm font-medium",
153
- children: "Project"
158
+ children: M.team ? "Team" : "Project"
154
159
  }), /* @__PURE__ */ h("div", {
155
160
  className: "mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground",
156
- children: N.find((e) => e.id === M.projectId)?.name ?? M.projectId
161
+ children: M.team ? M.team.name : N.find((e) => e.id === M.projectId)?.name ?? M.projectId
157
162
  })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
158
163
  className: "text-sm font-medium",
159
164
  children: "Status"
@@ -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, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { SprintLifecycleActions } from '../sprints/SprintControls';\nimport { throwOnError } from '../utils/mutationResult';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\n\nconst GET_SPRINT_FOR_EDIT = gql`\n query GetSprintForEdit($id: ID!, $workspaceId: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n status\n startDate\n endDate\n goal\n capacity\n }\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst UPDATE_SPRINT = gql`\n mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {\n planmagnetUpdateSprint(id: $id, input: $input) {\n id\n name\n status\n startDate\n endDate\n goal\n capacity\n }\n }\n`;\n\nexport function ProjectsPlanningEditPagePage() {\n const { workspaceId } = usePlanMagnet();\n const sprintId = useEntityId('sprintId');\n const navigate = usePlanMagnetNavigate();\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_SPRINT_FOR_EDIT, {\n variables: { id: sprintId, workspaceId: workspaceId ?? 'default' },\n skip: !sprintId,\n });\n\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const sprint = data?.planmagnetSprint;\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const defaultForm = useMemo(\n () => ({\n name: sprint?.name ?? '',\n startDate: sprint?.startDate ? sprint.startDate.split('T')[0] : '',\n endDate: sprint?.endDate ? sprint.endDate.split('T')[0] : '',\n goal: sprint?.goal ?? '',\n capacity: sprint?.capacity?.toString() ?? '',\n }),\n [sprint]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n // Keep ONLY the fields the user actually edited. Callers pass the whole form\n // ({ ...form, field }); copying every field made untouched dates stale\n // snapshots, so after a refetch (e.g. a CONFLICT) a retry re-sent the old\n // dates and silently reverted a teammate's replan (BOFF-7918 review).\n const setForm = (updates: Record<string, string>) =>\n setFormOverrides((prev) => {\n const next = { ...prev };\n for (const [key, value] of Object.entries(updates)) {\n if (value !== form[key as keyof typeof form]) next[key] = value;\n }\n return next;\n });\n\n const [saveError, setSaveError] = useState<string | null>(null);\n // BOFF-7833: the merged form is the plan the sprint would end up with (one side edited is compared with\n // the stored other side); the service enforces the same rules.\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n const capacity = parseIntField(form.capacity, { min: 0 });\n const [updateSprint, { loading: saving }] = useMutation(UPDATE_SPRINT, {\n refetchQueries: ['GetSprints', 'GetSprintDetail', 'GetSprintForEdit', 'GetDashboardData'],\n });\n\n const handleSave = async () => {\n if (!sprint || invalidDates || capacity.invalid) return;\n setSaveError(null);\n setSuccessMessage(null);\n // Send the planned dates only when the user changed them (BOFF-7918).\n // Every change to a planned date is recorded in the sprint's plan history,\n // so re-sending unchanged dates on a rename must not look like a replan.\n const dateChanges = {\n ...(form.startDate !== defaultForm.startDate && form.startDate\n ? { startDate: form.startDate }\n : {}),\n ...(form.endDate !== defaultForm.endDate && form.endDate ? { endDate: form.endDate } : {}),\n };\n try {\n await throwOnError(\n updateSprint({\n variables: {\n id: sprint.id,\n input: {\n name: form.name || undefined,\n ...dateChanges,\n goal: form.goal || undefined,\n capacity: capacity.value,\n },\n },\n })\n );\n // Only after the service ACCEPTED it: under fe-libs' errorPolicy 'all' a\n // refused save resolves, and onCompleted reported success for it.\n setSuccessMessage('Sprint updated successfully.');\n setFormOverrides({});\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The sprint could not be saved.');\n }\n };\n\n if (!sprintId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No planning session selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-destructive\">Error loading sprint: {error.message}</div>;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Back to the sprint\"\n onClick={() => navigate(detailPath('/projects/planning-detail-page', sprintId))}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Planning Session</h1>\n <p className=\"text-sm text-muted-foreground\">{sprint.name}</p>\n </div>\n </div>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || invalidDates || capacity.invalid}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n\n {successMessage && (\n <div role=\"status\" className=\"p-3 rounded-md text-sm bg-muted text-status-success-text\">\n {successMessage}\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md text-sm bg-destructive/10 text-destructive\">\n Error: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Session Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"text-sm font-medium\">Project</label>\n <div className=\"mt-1 w-full rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground\">\n {projects.find((p: { id: string; name: string }) => p.id === sprint.projectId)\n ?.name ?? sprint.projectId}\n </div>\n </div>\n <div>\n {/* Status is not an editable field: planmagnetUpdateSprint does not\n accept it, and sending it failed EVERY save on this page. It\n changes only through the guarded lifecycle actions. */}\n <label className=\"text-sm font-medium\">Status</label>\n <div className=\"mt-1 flex items-start justify-between gap-2\">\n <span className=\"rounded-md border border-input bg-muted px-3 py-2 text-sm text-muted-foreground capitalize\">\n {sprint.status.toLowerCase().replace(/_/g, ' ')}\n </span>\n <SprintLifecycleActions\n sprintId={sprint.id}\n sprintName={sprint.name}\n status={sprint.status}\n />\n </div>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label htmlFor=\"sprint-start\" className=\"text-sm font-medium\">\n Start Date\n </label>\n <Input\n id=\"sprint-start\"\n type=\"date\"\n value={form.startDate}\n max={form.endDate || undefined}\n onChange={(e) => setForm({ ...form, startDate: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-end\" className=\"text-sm font-medium\">\n End Date\n </label>\n <Input\n id=\"sprint-end\"\n type=\"date\"\n value={form.endDate}\n min={form.startDate || undefined}\n onChange={(e) => setForm({ ...form, endDate: e.target.value })}\n aria-invalid={invalidDates}\n className=\"mt-1\"\n />\n </div>\n </div>\n {invalidDates && (\n <p role=\"alert\" className=\"text-sm text-destructive\">\n The end date is before the start date.\n </p>\n )}\n <div>\n <label className=\"text-sm font-medium\">Goal</label>\n <textarea\n className=\"mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[60px]\"\n value={form.goal}\n onChange={(e) => setForm({ ...form, goal: e.target.value })}\n placeholder=\"Sprint goal...\"\n />\n </div>\n <div>\n <label htmlFor=\"sprint-capacity\" className=\"text-sm font-medium\">\n Capacity (story points)\n </label>\n <Input\n id=\"sprint-capacity\"\n type=\"number\"\n min={0}\n step={1}\n value={form.capacity}\n onChange={(e) => setForm({ ...form, capacity: e.target.value })}\n aria-invalid={capacity.invalid}\n className=\"mt-1\"\n placeholder=\"e.g. 40\"\n />\n {capacity.invalid && (\n <p role=\"alert\" className=\"mt-1 text-xs text-destructive\">\n Capacity must be a whole number, 0 or more.\n </p>\n )}\n </div>\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ProjectsPlanningEditPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsPlanningEditPagePage.tsx"],"sourcesContent":["// eslint-disable-next-line @typescript-eslint/ban-ts-comment\n// @ts-nocheck\nimport { useState, useMemo } from 'react';\nimport { gql } from '@apollo/client';\nimport { useQuery, useMutation } from '@apollo/client/react';\nimport { Card, CardContent, CardHeader, CardTitle, Button, Input } from '@burdenoff/fe-libs/ui';\nimport { Save, ArrowLeft } from 'lucide-react';\nimport { usePlanMagnet, usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { detailPath } from '../utils/rowNavigation';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { SprintLifecycleActions } from '../sprints/SprintControls';\nimport { throwOnError } from '../utils/mutationResult';\nimport { endBeforeStart, parseIntField } from '../utils/formValidation';\n\nconst GET_SPRINT_FOR_EDIT = gql`\n query GetSprintForEdit($id: ID!, $workspaceId: ID!) {\n planmagnetSprint(id: $id) {\n id\n name\n projectId\n teamId\n team {\n id\n name\n }\n status\n startDate\n endDate\n goal\n capacity\n }\n planmagnetProjects(workspaceId: $workspaceId, pagination: { first: 100 }) {\n items {\n id\n name\n }\n }\n }\n`;\n\nconst UPDATE_SPRINT = gql`\n mutation UpdateSprint($id: ID!, $input: PlanmagnetUpdateSprintInput!) {\n planmagnetUpdateSprint(id: $id, input: $input) {\n id\n name\n status\n startDate\n endDate\n goal\n capacity\n }\n }\n`;\n\nexport function ProjectsPlanningEditPagePage() {\n const { workspaceId } = usePlanMagnet();\n const sprintId = useEntityId('sprintId');\n const navigate = usePlanMagnetNavigate();\n\n const {\n data,\n loading,\n error,\n } = // eslint-disable-next-line @typescript-eslint/no-explicit-any\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n useQuery<Record<string, any>>(GET_SPRINT_FOR_EDIT, {\n variables: { id: sprintId, workspaceId: workspaceId ?? 'default' },\n skip: !sprintId,\n });\n\n const [successMessage, setSuccessMessage] = useState<string | null>(null);\n\n const sprint = data?.planmagnetSprint;\n const projects = data?.planmagnetProjects?.items ?? [];\n\n const defaultForm = useMemo(\n () => ({\n name: sprint?.name ?? '',\n startDate: sprint?.startDate ? sprint.startDate.split('T')[0] : '',\n endDate: sprint?.endDate ? sprint.endDate.split('T')[0] : '',\n goal: sprint?.goal ?? '',\n capacity: sprint?.capacity?.toString() ?? '',\n }),\n [sprint]\n );\n\n const [formOverrides, setFormOverrides] = useState<Record<string, string>>({});\n const form = { ...defaultForm, ...formOverrides };\n // Keep ONLY the fields the user actually edited. Callers pass the whole form\n // ({ ...form, field }); copying every field made untouched dates stale\n // snapshots, so after a refetch (e.g. a CONFLICT) a retry re-sent the old\n // dates and silently reverted a teammate's replan (BOFF-7918 review).\n const setForm = (updates: Record<string, string>) =>\n setFormOverrides((prev) => {\n const next = { ...prev };\n for (const [key, value] of Object.entries(updates)) {\n if (value !== form[key as keyof typeof form]) next[key] = value;\n }\n return next;\n });\n\n const [saveError, setSaveError] = useState<string | null>(null);\n // BOFF-7833: the merged form is the plan the sprint would end up with (one side edited is compared with\n // the stored other side); the service enforces the same rules.\n const invalidDates = endBeforeStart(form.startDate, form.endDate);\n const capacity = parseIntField(form.capacity, { min: 0 });\n const [updateSprint, { loading: saving }] = useMutation(UPDATE_SPRINT, {\n refetchQueries: ['GetSprints', 'GetSprintDetail', 'GetSprintForEdit', 'GetDashboardData'],\n });\n\n const handleSave = async () => {\n if (!sprint || invalidDates || capacity.invalid) return;\n setSaveError(null);\n setSuccessMessage(null);\n // Send the planned dates only when the user changed them (BOFF-7918).\n // Every change to a planned date is recorded in the sprint's plan history,\n // so re-sending unchanged dates on a rename must not look like a replan.\n const dateChanges = {\n ...(form.startDate !== defaultForm.startDate && form.startDate\n ? { startDate: form.startDate }\n : {}),\n ...(form.endDate !== defaultForm.endDate && form.endDate ? { endDate: form.endDate } : {}),\n };\n try {\n await throwOnError(\n updateSprint({\n variables: {\n id: sprint.id,\n input: {\n name: form.name || undefined,\n ...dateChanges,\n goal: form.goal || undefined,\n capacity: capacity.value,\n },\n },\n })\n );\n // Only after the service ACCEPTED it: under fe-libs' errorPolicy 'all' a\n // refused save resolves, and onCompleted reported success for it.\n setSuccessMessage('Sprint updated successfully.');\n setFormOverrides({});\n } catch (e) {\n setSaveError(e instanceof Error ? e.message : 'The sprint could not be saved.');\n }\n };\n\n if (!sprintId)\n return (\n <div className=\"p-6 text-muted-foreground\">\n No planning session selected. Open one from the list.\n </div>\n );\n // Only before the first load: a refetch after a sprint action must not blank\n // the page (it unmounted the controls mid-action).\n if (loading && !data)\n return (\n <div className=\"flex items-center justify-center p-12\">\n <div className=\"animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full\" />\n </div>\n );\n if (error)\n return <div className=\"p-6 text-destructive\">Error loading sprint: {error.message}</div>;\n if (!sprint) return <div className=\"p-6 text-muted-foreground\">Planning session not found.</div>;\n\n return (\n <div className=\"space-y-6 p-6 max-w-4xl\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-3\">\n <Button\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Back to the sprint\"\n onClick={() => navigate(detailPath('/projects/planning-detail-page', sprintId))}\n >\n <ArrowLeft className=\"h-4 w-4\" />\n </Button>\n <div>\n <h1 className=\"text-2xl font-semibold\">Edit Planning Session</h1>\n <p className=\"text-sm text-muted-foreground\">{sprint.name}</p>\n </div>\n </div>\n <Button\n size=\"sm\"\n onClick={handleSave}\n disabled={saving || invalidDates || capacity.invalid}\n >\n <Save className=\"mr-1 h-4 w-4\" />\n {saving ? 'Saving...' : 'Save Changes'}\n </Button>\n </div>\n\n {successMessage && (\n <div role=\"status\" className=\"p-3 rounded-md text-sm bg-muted text-status-success-text\">\n {successMessage}\n </div>\n )}\n {saveError && (\n <div role=\"alert\" className=\"p-3 rounded-md text-sm bg-destructive/10 text-destructive\">\n Error: {saveError}\n </div>\n )}\n\n <Card>\n <CardHeader>\n <CardTitle>Session Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n <div>\n <label className=\"text-sm font-medium\">Name</label>\n <Input\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n className=\"mt-1\"\n />\n </div>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n {/* A sprint's scope is PROJECT xor TEAM and is immutable after creation (BOFF-8044) —\n this field is read-only either way. A team sprint has no projectId, so it used to\n render blank here; show the team instead. */}\n <label className=\"text-sm font-medium\">{sprint.team ? 'Team' : '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 {sprint.team\n ? sprint.team.name\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":"ProjectsPlanningPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.tsx"],"names":[],"mappings":"AA8CA,wBAAgB,wBAAwB,4CAuIvC"}
1
+ {"version":3,"file":"ProjectsPlanningPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsPlanningPagePage.tsx"],"names":[],"mappings":"AAoDA,wBAAgB,wBAAwB,4CA0JvC"}
@@ -1,15 +1,17 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
- import { detailPath as n, rowLinkProps as r } from "../utils/rowNavigation.js";
3
- import { NewSprintDialog as i } from "../sprints/SprintControls.js";
4
- import { CalendarClock as a, FolderKanban as o } from "lucide-react";
5
- import { gql as s } from "@apollo/client";
6
- import { useQuery as c } from "@apollo/client/react";
7
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
- import { Badge as d, Card as f, CardContent as p, CardHeader as m, CardTitle as h } from "@burdenoff/fe-libs/ui";
2
+ import { TeamSelect as n } from "../controls/Pickers.js";
3
+ import { detailPath as r, rowLinkProps as i } from "../utils/rowNavigation.js";
4
+ import { NewSprintDialog as a } from "../sprints/SprintControls.js";
5
+ import { useState as o } from "react";
6
+ import { CalendarClock as s, FolderKanban as c, Users as l } from "lucide-react";
7
+ import { gql as u } from "@apollo/client";
8
+ import { useQuery as d } from "@apollo/client/react";
9
+ import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
10
+ import { Badge as h, Card as g, CardContent as _, CardHeader as v, CardTitle as y } from "@burdenoff/fe-libs/ui";
9
11
  //#region src/planmagnet/pages/ProjectsPlanningPagePage.tsx
10
- var g = s`
11
- query GetSprints($workspaceId: ID!) {
12
- planmagnetSprints(workspaceId: $workspaceId, pagination: { first: 100 }) {
12
+ var b = u`
13
+ query GetSprints($workspaceId: ID!, $filter: PlanmagnetSprintFilter) {
14
+ planmagnetSprints(workspaceId: $workspaceId, filter: $filter, pagination: { first: 100 }) {
13
15
  items {
14
16
  id
15
17
  name
@@ -18,6 +20,10 @@ var g = s`
18
20
  id
19
21
  name
20
22
  }
23
+ team {
24
+ id
25
+ name
26
+ }
21
27
  status
22
28
  startDate
23
29
  endDate
@@ -33,107 +39,121 @@ var g = s`
33
39
  totalCount
34
40
  }
35
41
  }
36
- `, _ = {
42
+ `, x = {
37
43
  PLANNING: "bg-blue-100 text-blue-800",
38
44
  ACTIVE: "bg-green-100 text-green-800",
39
45
  REVIEW: "bg-purple-100 text-purple-800",
40
46
  COMPLETED: "bg-gray-100 text-gray-800",
41
47
  CANCELLED: "bg-red-100 text-red-800"
42
48
  };
43
- function v() {
44
- let { workspaceId: s } = e(), v = t(), { data: y, loading: b, error: x } = c(g, { variables: { workspaceId: s ?? "default" } });
45
- if (b && !y) return /* @__PURE__ */ l("div", {
49
+ function S() {
50
+ let { workspaceId: u } = e(), S = t(), [C, w] = o(""), { data: T, loading: E, error: D } = d(b, { variables: {
51
+ workspaceId: u ?? "default",
52
+ filter: C ? { teamId: C } : void 0
53
+ } });
54
+ if (E && !T) return /* @__PURE__ */ p("div", {
46
55
  className: "flex items-center justify-center p-12",
47
- children: /* @__PURE__ */ l("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
56
+ children: /* @__PURE__ */ p("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
48
57
  });
49
- if (x) return /* @__PURE__ */ u("div", {
58
+ if (D) return /* @__PURE__ */ m("div", {
50
59
  className: "p-6 text-red-500",
51
- children: ["Error loading planning sessions: ", x.message]
60
+ children: ["Error loading planning sessions: ", D.message]
52
61
  });
53
- let S = y?.planmagnetSprints?.items ?? [];
54
- return /* @__PURE__ */ u("div", {
62
+ let O = T?.planmagnetSprints?.items ?? [];
63
+ return /* @__PURE__ */ m("div", {
55
64
  className: "space-y-6 p-6",
56
65
  children: [
57
- /* @__PURE__ */ u("div", {
66
+ /* @__PURE__ */ m("div", {
58
67
  className: "flex items-center justify-between",
59
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h1", {
68
+ children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("h1", {
60
69
  className: "text-2xl font-semibold",
61
70
  children: "Planning Sessions"
62
- }), /* @__PURE__ */ u("p", {
71
+ }), /* @__PURE__ */ m("p", {
63
72
  className: "text-sm text-muted-foreground",
64
- children: [y?.planmagnetSprints?.totalCount ?? S.length, " sprints across all projects"]
65
- })] }), /* @__PURE__ */ l(i, { onCreated: (e) => v(n("/projects/planning-detail-page", e)) })]
73
+ children: [T?.planmagnetSprints?.totalCount ?? O.length, " sprints across all projects"]
74
+ })] }), /* @__PURE__ */ m("div", {
75
+ className: "flex items-center gap-2",
76
+ children: [/* @__PURE__ */ p(n, {
77
+ value: C,
78
+ onChange: w,
79
+ noneLabel: "All teams"
80
+ }), /* @__PURE__ */ p(a, { onCreated: (e) => S(r("/projects/planning-detail-page", e)) })]
81
+ })]
66
82
  }),
67
- /* @__PURE__ */ l("div", {
83
+ /* @__PURE__ */ p("div", {
68
84
  className: "grid grid-cols-1 md:grid-cols-2 gap-4",
69
- children: S.map((e) => {
70
- let t = e.workItems.reduce((e, t) => e + (t.storyPoints ?? 0), 0), i = e.capacity ?? 0, s = i > 0 ? Math.round(t / i * 100) : 0;
71
- return /* @__PURE__ */ u(f, {
72
- ...r(() => v(n("/projects/planning-detail-page", e.id)), e.name),
73
- children: [/* @__PURE__ */ l(m, {
85
+ children: O.map((e) => {
86
+ let t = e.workItems.reduce((e, t) => e + (t.storyPoints ?? 0), 0), n = e.capacity ?? 0, a = n > 0 ? Math.round(t / n * 100) : 0;
87
+ return /* @__PURE__ */ m(g, {
88
+ ...i(() => S(r("/projects/planning-detail-page", e.id)), e.name),
89
+ children: [/* @__PURE__ */ p(v, {
74
90
  className: "pb-2",
75
- children: /* @__PURE__ */ u("div", {
91
+ children: /* @__PURE__ */ m("div", {
76
92
  className: "flex items-center justify-between",
77
- children: [/* @__PURE__ */ u(h, {
93
+ children: [/* @__PURE__ */ m(y, {
78
94
  className: "text-base flex items-center gap-2",
79
- children: [/* @__PURE__ */ l(a, { className: "h-4 w-4 text-primary" }), e.name]
80
- }), /* @__PURE__ */ l(d, {
95
+ children: [/* @__PURE__ */ p(s, { className: "h-4 w-4 text-primary" }), e.name]
96
+ }), /* @__PURE__ */ p(h, {
81
97
  variant: e.status === "COMPLETED" ? "default" : "secondary",
82
98
  className: "capitalize",
83
99
  children: e.status.toLowerCase().replace("_", " ")
84
100
  })]
85
101
  })
86
- }), /* @__PURE__ */ u(p, {
102
+ }), /* @__PURE__ */ m(_, {
87
103
  className: "space-y-3",
88
104
  children: [
89
- /* @__PURE__ */ u("div", {
105
+ /* @__PURE__ */ m("div", {
90
106
  className: "flex gap-2",
91
- children: [/* @__PURE__ */ l("span", {
92
- className: `text-xs px-2 py-0.5 rounded-full capitalize ${_[e.status] ?? "bg-gray-100"}`,
107
+ children: [/* @__PURE__ */ p("span", {
108
+ className: `text-xs px-2 py-0.5 rounded-full capitalize ${x[e.status] ?? "bg-gray-100"}`,
93
109
  children: e.status.toLowerCase().replace("_", " ")
94
- }), /* @__PURE__ */ u("span", {
110
+ }), /* @__PURE__ */ p("span", {
95
111
  className: "text-xs text-muted-foreground flex items-center gap-1",
96
- children: [/* @__PURE__ */ l(o, { className: "h-3 w-3" }), e.project?.name ?? "Unknown project"]
112
+ children: e.team ? /* @__PURE__ */ m(f, { children: [
113
+ /* @__PURE__ */ p(l, { className: "h-3 w-3" }),
114
+ "Team: ",
115
+ e.team.name
116
+ ] }) : /* @__PURE__ */ m(f, { children: [/* @__PURE__ */ p(c, { className: "h-3 w-3" }), e.project?.name ?? "Unknown project"] })
97
117
  })]
98
118
  }),
99
- e.goal && /* @__PURE__ */ l("p", {
119
+ e.goal && /* @__PURE__ */ p("p", {
100
120
  className: "text-sm text-muted-foreground",
101
121
  children: e.goal
102
122
  }),
103
- /* @__PURE__ */ u("div", {
123
+ /* @__PURE__ */ m("div", {
104
124
  className: "flex gap-3 text-xs text-muted-foreground",
105
- children: [/* @__PURE__ */ u("span", {
125
+ children: [/* @__PURE__ */ m("span", {
106
126
  className: "flex items-center gap-1",
107
127
  children: [
108
- /* @__PURE__ */ l(a, { className: "h-3 w-3" }),
128
+ /* @__PURE__ */ p(s, { className: "h-3 w-3" }),
109
129
  e.startDate ? new Date(e.startDate).toLocaleDateString() : "—",
110
130
  " -",
111
131
  " ",
112
132
  e.endDate ? new Date(e.endDate).toLocaleDateString() : "—"
113
133
  ]
114
- }), /* @__PURE__ */ u("span", { children: [e.workItems.length, " work items"] })]
134
+ }), /* @__PURE__ */ m("span", { children: [e.workItems.length, " work items"] })]
115
135
  }),
116
- i > 0 && /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ u("div", {
136
+ n > 0 && /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ m("div", {
117
137
  className: "flex justify-between text-xs mb-1",
118
- children: [/* @__PURE__ */ l("span", {
138
+ children: [/* @__PURE__ */ p("span", {
119
139
  className: "text-muted-foreground",
120
140
  children: "Capacity"
121
- }), /* @__PURE__ */ u("span", {
141
+ }), /* @__PURE__ */ m("span", {
122
142
  className: "font-medium",
123
143
  children: [
124
144
  t,
125
145
  "/",
126
- i,
146
+ n,
127
147
  " pts (",
128
- s,
148
+ a,
129
149
  "%)"
130
150
  ]
131
151
  })]
132
- }), /* @__PURE__ */ l("div", {
152
+ }), /* @__PURE__ */ p("div", {
133
153
  className: "w-full h-2 rounded-full bg-muted overflow-hidden",
134
- children: /* @__PURE__ */ l("div", {
135
- className: `h-full rounded-full ${s > 90 ? "bg-red-400" : "bg-primary"}`,
136
- style: { width: `${Math.min(s, 100)}%` }
154
+ children: /* @__PURE__ */ p("div", {
155
+ className: `h-full rounded-full ${a > 90 ? "bg-red-400" : "bg-primary"}`,
156
+ style: { width: `${Math.min(a, 100)}%` }
137
157
  })
138
158
  })] })
139
159
  ]
@@ -141,7 +161,7 @@ function v() {
141
161
  }, e.id);
142
162
  })
143
163
  }),
144
- S.length === 0 && /* @__PURE__ */ l("p", {
164
+ O.length === 0 && /* @__PURE__ */ p("p", {
145
165
  className: "text-center text-muted-foreground py-8",
146
166
  children: "No planning sessions found."
147
167
  })
@@ -149,6 +169,6 @@ function v() {
149
169
  });
150
170
  }
151
171
  //#endregion
152
- export { v as ProjectsPlanningPagePage };
172
+ export { S as ProjectsPlanningPagePage };
153
173
 
154
174
  //# sourceMappingURL=ProjectsPlanningPagePage.js.map