@burdenoff/microfe-planmagnet 2026.928.4 → 2026.928.5

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 (183) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +117 -93
  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 +156 -76
  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/PortfolioControls.js +7 -7
  58. package/dist/planmagnet/controls/ProgramControls.js +19 -19
  59. package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
  60. package/dist/planmagnet/controls/ProjectControls.js +54 -47
  61. package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
  62. package/dist/planmagnet/controls/ReleaseControls.js +7 -7
  63. package/dist/planmagnet/controls/RiskControls.d.ts +27 -0
  64. package/dist/planmagnet/controls/RiskControls.d.ts.map +1 -0
  65. package/dist/planmagnet/controls/RiskControls.js +346 -0
  66. package/dist/planmagnet/controls/RiskControls.js.map +1 -0
  67. package/dist/planmagnet/controls/TemplateControls.d.ts +15 -0
  68. package/dist/planmagnet/controls/TemplateControls.d.ts.map +1 -0
  69. package/dist/planmagnet/controls/TemplateControls.js +55 -0
  70. package/dist/planmagnet/controls/TemplateControls.js.map +1 -0
  71. package/dist/planmagnet/controls/WorkItemControls.js +1 -1
  72. package/dist/planmagnet/create/CreateDialogs.d.ts +20 -0
  73. package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
  74. package/dist/planmagnet/create/CreateDialogs.js +455 -116
  75. package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
  76. package/dist/planmagnet/dependency/DependencyPanel.d.ts +6 -0
  77. package/dist/planmagnet/dependency/DependencyPanel.d.ts.map +1 -0
  78. package/dist/planmagnet/dependency/DependencyPanel.js +310 -0
  79. package/dist/planmagnet/dependency/DependencyPanel.js.map +1 -0
  80. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts +2 -0
  81. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts.map +1 -0
  82. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js +171 -0
  83. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js.map +1 -0
  84. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts +2 -0
  85. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts.map +1 -0
  86. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js +232 -0
  87. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js.map +1 -0
  88. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts +2 -0
  89. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts.map +1 -0
  90. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js +93 -0
  91. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js.map +1 -0
  92. package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.js +5 -5
  93. package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +9 -9
  94. package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.js +6 -6
  95. package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.js +4 -4
  96. package/dist/planmagnet/pages/ProgramsEditProgramPagePage.js +6 -6
  97. package/dist/planmagnet/pages/ProgramsProgramsPagePage.js +5 -5
  98. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts +2 -0
  99. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts.map +1 -0
  100. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js +315 -0
  101. package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js.map +1 -0
  102. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts +2 -0
  103. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts.map +1 -0
  104. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +186 -0
  105. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js.map +1 -0
  106. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts +2 -0
  107. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts.map +1 -0
  108. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js +164 -0
  109. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js.map +1 -0
  110. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts +2 -0
  111. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts.map +1 -0
  112. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js +348 -0
  113. package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js.map +1 -0
  114. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +10 -10
  115. package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +4 -4
  116. package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +4 -4
  117. package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +6 -6
  118. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  119. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +319 -203
  120. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  121. package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
  122. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +81 -72
  123. package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
  124. package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts +2 -0
  125. package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts.map +1 -0
  126. package/dist/planmagnet/pages/RisksRiskDetailPagePage.js +278 -0
  127. package/dist/planmagnet/pages/RisksRiskDetailPagePage.js.map +1 -0
  128. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts +2 -0
  129. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts.map +1 -0
  130. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js +191 -0
  131. package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js.map +1 -0
  132. package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +5 -5
  133. package/dist/planmagnet/pages/TasksEditTaskPagePage.js +4 -4
  134. package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
  135. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +161 -137
  136. package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
  137. package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
  138. package/dist/planmagnet/pages/TasksTasksPagePage.js +124 -113
  139. package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
  140. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts +2 -0
  141. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts.map +1 -0
  142. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +263 -0
  143. package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js.map +1 -0
  144. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts +2 -0
  145. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts.map +1 -0
  146. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js +254 -0
  147. package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js.map +1 -0
  148. package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts +2 -0
  149. package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts.map +1 -0
  150. package/dist/planmagnet/pages/TasksTimesheetsPagePage.js +165 -0
  151. package/dist/planmagnet/pages/TasksTimesheetsPagePage.js.map +1 -0
  152. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts +2 -0
  153. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts.map +1 -0
  154. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js +316 -0
  155. package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js.map +1 -0
  156. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts +2 -0
  157. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts.map +1 -0
  158. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js +208 -0
  159. package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js.map +1 -0
  160. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts +0 -5
  161. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
  162. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +47 -89
  163. package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
  164. package/dist/planmagnet/roadmaps/RoadmapControls.js +24 -24
  165. package/dist/planmagnet/time/TaskTimePanel.d.ts +7 -0
  166. package/dist/planmagnet/time/TaskTimePanel.d.ts.map +1 -0
  167. package/dist/planmagnet/time/TaskTimePanel.js +104 -0
  168. package/dist/planmagnet/time/TaskTimePanel.js.map +1 -0
  169. package/dist/planmagnet/time/TimeEntryControls.d.ts +37 -0
  170. package/dist/planmagnet/time/TimeEntryControls.d.ts.map +1 -0
  171. package/dist/planmagnet/time/TimeEntryControls.js +338 -0
  172. package/dist/planmagnet/time/TimeEntryControls.js.map +1 -0
  173. package/dist/planmagnet/time/TimesheetControls.d.ts +18 -0
  174. package/dist/planmagnet/time/TimesheetControls.d.ts.map +1 -0
  175. package/dist/planmagnet/time/TimesheetControls.js +244 -0
  176. package/dist/planmagnet/time/TimesheetControls.js.map +1 -0
  177. package/dist/planmagnet/time/timeShared.d.ts +29 -0
  178. package/dist/planmagnet/time/timeShared.d.ts.map +1 -0
  179. package/dist/planmagnet/time/timeShared.js +34 -0
  180. package/dist/planmagnet/time/timeShared.js.map +1 -0
  181. package/dist/planmagnet/utils/rowNavigation.js +5 -2
  182. package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
  183. 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 { enumLabel as r } from "../controls/WorkItemControls.js";
3
+ import { enumLabel as n } from "../controls/WorkItemControls.js";
4
+ import { detailPath as r } from "../utils/rowNavigation.js";
5
5
  import { PortfolioHeaderActions as i } from "../controls/PortfolioControls.js";
6
6
  import { AlertTriangle as a, Briefcase as o, Clock as s, Loader2 as c, Target as l } from "lucide-react";
7
7
  import { useParams as u } from "react-router";
@@ -93,12 +93,12 @@ function S() {
93
93
  /* @__PURE__ */ p(h, {
94
94
  variant: "outline",
95
95
  className: "text-xs",
96
- children: r(D.status)
96
+ children: n(D.status)
97
97
  })
98
98
  ]
99
99
  }), D.alignment && /* @__PURE__ */ p("p", {
100
100
  className: "text-sm text-muted-foreground mt-1",
101
- children: D.alignment === "CUSTOM" ? D.alignmentLabel ?? "Custom alignment" : r(D.alignment)
101
+ children: D.alignment === "CUSTOM" ? D.alignmentLabel ?? "Custom alignment" : n(D.alignment)
102
102
  })] }), /* @__PURE__ */ p(i, {
103
103
  portfolioId: D.id,
104
104
  name: D.name,
@@ -216,7 +216,7 @@ function S() {
216
216
  children: "Projects by Status"
217
217
  }), /* @__PURE__ */ p("p", {
218
218
  className: "text-sm mt-1",
219
- children: Object.keys(O.projectsByStatus).length > 0 ? Object.entries(O.projectsByStatus).map(([e, t]) => `${r(e)}: ${t}`).join(", ") : "No projects yet"
219
+ children: Object.keys(O.projectsByStatus).length > 0 ? Object.entries(O.projectsByStatus).map(([e, t]) => `${n(e)}: ${t}`).join(", ") : "No projects yet"
220
220
  })]
221
221
  })
222
222
  ]
@@ -237,14 +237,14 @@ function S() {
237
237
  }), D.programs.map((e) => /* @__PURE__ */ m("button", {
238
238
  type: "button",
239
239
  className: "flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted",
240
- onClick: () => d(n("/products/program-detail-page", e.id)),
240
+ onClick: () => d(r("/products/program-detail-page", e.id)),
241
241
  children: [/* @__PURE__ */ p("span", {
242
242
  className: "text-sm font-medium",
243
243
  children: e.name
244
244
  }), /* @__PURE__ */ p(h, {
245
245
  variant: "outline",
246
246
  className: "text-xs",
247
- children: r(e.status)
247
+ children: n(e.status)
248
248
  })]
249
249
  }, e.id))]
250
250
  })] }),
@@ -260,14 +260,14 @@ function S() {
260
260
  }), D.projects.map((e) => /* @__PURE__ */ m("button", {
261
261
  type: "button",
262
262
  className: "flex w-full items-center justify-between rounded-md border p-3 text-left hover:bg-muted",
263
- onClick: () => d(n("/projects/project-detail-page", e.id)),
263
+ onClick: () => d(r("/projects/project-detail-page", e.id)),
264
264
  children: [/* @__PURE__ */ p("span", {
265
265
  className: "text-sm font-medium",
266
266
  children: e.name
267
267
  }), /* @__PURE__ */ p(h, {
268
268
  variant: "outline",
269
269
  className: "text-xs",
270
- children: r(e.status)
270
+ children: n(e.status)
271
271
  })]
272
272
  }, e.id))]
273
273
  })] })
@@ -1,7 +1,7 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as n } from "../people/People.js";
3
- import { detailPath as r, rowLinkProps as i } from "../utils/rowNavigation.js";
4
- import { enumLabel as a } from "../controls/WorkItemControls.js";
3
+ import { enumLabel as r } from "../controls/WorkItemControls.js";
4
+ import { detailPath as i, rowLinkProps as a } from "../utils/rowNavigation.js";
5
5
  import { PORTFOLIO_STATUSES as o } from "../controls/PortfolioControls.js";
6
6
  import { useState as s } from "react";
7
7
  import { Briefcase as c, Loader2 as l, Plus as u } from "lucide-react";
@@ -93,14 +93,14 @@ function C() {
93
93
  "aria-pressed": w === e,
94
94
  onClick: () => T(e),
95
95
  className: `px-2 py-1 text-xs rounded-md capitalize ${w === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
96
- children: e === "all" ? "All" : a(e)
96
+ children: e === "all" ? "All" : r(e)
97
97
  }, e))
98
98
  })]
99
99
  }),
100
100
  /* @__PURE__ */ p("div", {
101
101
  className: "grid grid-cols-1 md:grid-cols-2 gap-4",
102
102
  children: j.map((e) => /* @__PURE__ */ m(_, {
103
- ...i(() => d(r("/products/portfolio-detail-page", e.id)), e.name),
103
+ ...a(() => d(i("/products/portfolio-detail-page", e.id)), e.name),
104
104
  children: [/* @__PURE__ */ p(y, {
105
105
  className: "pb-2",
106
106
  children: /* @__PURE__ */ m("div", {
@@ -111,7 +111,7 @@ function C() {
111
111
  }), /* @__PURE__ */ p(h, {
112
112
  variant: "outline",
113
113
  className: "text-xs",
114
- children: a(e.status)
114
+ children: r(e.status)
115
115
  })]
116
116
  })
117
117
  }), /* @__PURE__ */ m(v, {
@@ -126,7 +126,7 @@ function C() {
126
126
  children: [e.alignment && /* @__PURE__ */ p(h, {
127
127
  variant: "secondary",
128
128
  className: "text-xs",
129
- children: e.alignment === "CUSTOM" ? e.alignmentLabel ?? "Custom" : a(e.alignment)
129
+ children: e.alignment === "CUSTOM" ? e.alignmentLabel ?? "Custom" : r(e.alignment)
130
130
  }), e.fiscalPeriod && /* @__PURE__ */ p(h, {
131
131
  variant: "outline",
132
132
  className: "text-xs",
@@ -1,7 +1,7 @@
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 { endBeforeStart as r } from "../utils/formValidation.js";
4
- import { enumLabel as i } from "../controls/WorkItemControls.js";
3
+ import { enumLabel as r } from "../controls/WorkItemControls.js";
4
+ import { endBeforeStart as i } from "../utils/formValidation.js";
5
5
  import { PROGRAM_STATUSES as a } from "../controls/ProgramControls.js";
6
6
  import { useState as o } from "react";
7
7
  import { FolderKanban as s, Loader2 as c, Save as l, X as u } from "lucide-react";
@@ -28,7 +28,7 @@ function S() {
28
28
  budgetTotal: "",
29
29
  currency: "",
30
30
  tags: ""
31
- }), [T, E] = o(null), [D, { loading: O }] = f(x, { refetchQueries: ["ListPrograms"] }), k = r(C.startDate, C.endDate), A = C.budgetTotal !== "" && Number(C.budgetTotal) < 0;
31
+ }), [T, E] = o(null), [D, { loading: O }] = f(x, { refetchQueries: ["ListPrograms"] }), k = i(C.startDate, C.endDate), A = C.budgetTotal !== "" && Number(C.budgetTotal) < 0;
32
32
  return /* @__PURE__ */ m("div", {
33
33
  className: "space-y-6 p-6 max-w-4xl",
34
34
  children: [
@@ -123,7 +123,7 @@ function S() {
123
123
  }),
124
124
  children: a.map((e) => /* @__PURE__ */ p("option", {
125
125
  value: e,
126
- children: i(e)
126
+ children: r(e)
127
127
  }, e))
128
128
  })] }),
129
129
  /* @__PURE__ */ m("div", {
@@ -1,8 +1,8 @@
1
1
  import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { throwOnError as t } from "../utils/mutationResult.js";
3
- import { detailPath as n } from "../utils/rowNavigation.js";
4
- import { endBeforeStart as r } from "../utils/formValidation.js";
5
- import { enumLabel as i } from "../controls/WorkItemControls.js";
3
+ import { enumLabel as n } from "../controls/WorkItemControls.js";
4
+ import { detailPath as r } from "../utils/rowNavigation.js";
5
+ import { endBeforeStart as i } from "../utils/formValidation.js";
6
6
  import { PROGRAM_STATUSES as a } from "../controls/ProgramControls.js";
7
7
  import { useMemo as o, useState as s } from "react";
8
8
  import { ArrowLeft as c, Loader2 as l, Save as u } from "lucide-react";
@@ -65,7 +65,7 @@ function T() {
65
65
  }), [F]), [L, R] = s({}), z = {
66
66
  ...I,
67
67
  ...L
68
- }, B = (e) => R(e), V = a.includes(z.status) ? a : [z.status, ...a], H = r(z.startDate, z.endDate), U = z.budgetTotal !== "" && Number(z.budgetTotal) < 0;
68
+ }, B = (e) => R(e), V = a.includes(z.status) ? a : [z.status, ...a], H = i(z.startDate, z.endDate), U = z.budgetTotal !== "" && Number(z.budgetTotal) < 0;
69
69
  return D && !E ? /* @__PURE__ */ h("div", {
70
70
  className: "flex items-center justify-center p-12",
71
71
  children: /* @__PURE__ */ h(l, { className: "h-8 w-8 animate-spin text-muted-foreground" })
@@ -83,7 +83,7 @@ function T() {
83
83
  variant: "ghost",
84
84
  size: "sm",
85
85
  "aria-label": "Back to the program",
86
- onClick: () => f(n("/products/program-detail-page", T)),
86
+ onClick: () => f(r("/products/program-detail-page", T)),
87
87
  children: /* @__PURE__ */ h(c, { className: "h-4 w-4" })
88
88
  }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("h1", {
89
89
  className: "text-2xl font-semibold",
@@ -181,7 +181,7 @@ function T() {
181
181
  }),
182
182
  children: V.map((e) => /* @__PURE__ */ h("option", {
183
183
  value: e,
184
- children: i(e)
184
+ children: n(e)
185
185
  }, e))
186
186
  })] }),
187
187
  /* @__PURE__ */ g("div", {
@@ -1,7 +1,7 @@
1
1
  import { usePlanMagnet as e, usePlanMagnetNavigate as t } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as n } from "../people/People.js";
3
- import { detailPath as r, rowLinkProps as i } from "../utils/rowNavigation.js";
4
- import { enumLabel as a } from "../controls/WorkItemControls.js";
3
+ import { enumLabel as r } from "../controls/WorkItemControls.js";
4
+ import { detailPath as i, rowLinkProps as a } from "../utils/rowNavigation.js";
5
5
  import { PROGRAM_STATUSES as o } from "../controls/ProgramControls.js";
6
6
  import { useState as s } from "react";
7
7
  import { FolderKanban as c, Loader2 as l, Plus as u } from "lucide-react";
@@ -95,14 +95,14 @@ function C() {
95
95
  "aria-pressed": w === e,
96
96
  onClick: () => T(e),
97
97
  className: `px-2 py-1 text-xs rounded-md capitalize ${w === e ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"}`,
98
- children: e === "all" ? "All" : a(e)
98
+ children: e === "all" ? "All" : r(e)
99
99
  }, e))
100
100
  })]
101
101
  }),
102
102
  /* @__PURE__ */ p("div", {
103
103
  className: "grid grid-cols-1 md:grid-cols-2 gap-4",
104
104
  children: j.map((e) => /* @__PURE__ */ m(_, {
105
- ...i(() => d(r("/products/program-detail-page", e.id)), e.name),
105
+ ...a(() => d(i("/products/program-detail-page", e.id)), e.name),
106
106
  children: [/* @__PURE__ */ p(y, {
107
107
  className: "pb-2",
108
108
  children: /* @__PURE__ */ m("div", {
@@ -113,7 +113,7 @@ function C() {
113
113
  }), /* @__PURE__ */ p(h, {
114
114
  variant: "outline",
115
115
  className: "text-xs",
116
- children: a(e.status)
116
+ children: r(e.status)
117
117
  })]
118
118
  })
119
119
  }), /* @__PURE__ */ m(v, {
@@ -0,0 +1,2 @@
1
+ export declare function ProjectsBudgetDetailPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ProjectsBudgetDetailPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectsBudgetDetailPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsBudgetDetailPagePage.tsx"],"names":[],"mappings":"AAsFA,wBAAgB,4BAA4B,4CAqP3C"}
@@ -0,0 +1,315 @@
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { PersonName as t } from "../people/People.js";
3
+ import { BUDGET_STATE_STYLE as n, budgetState as r, budgetUsage as i, money as a } from "../budgets/budgetMath.js";
4
+ import { useEntityId as o } from "../hooks/useEntityId.js";
5
+ import { enumLabel as s } from "../controls/WorkItemControls.js";
6
+ import { BudgetHeaderActions as c } from "../budgets/BudgetControls.js";
7
+ import { DeleteExpenseButton as l, EditExpenseDialog as u, LogExpenseDialog as d } from "../budgets/ExpenseControls.js";
8
+ import { ArrowLeft as f, Receipt as p } from "lucide-react";
9
+ import { gql as m } from "@apollo/client";
10
+ import { useQuery as h } from "@apollo/client/react";
11
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
12
+ import { Button as v, Card as y, CardContent as b, CardHeader as x, CardTitle as S } from "@burdenoff/fe-libs/ui";
13
+ //#region src/planmagnet/pages/ProjectsBudgetDetailPagePage.tsx
14
+ var C = m`
15
+ query GetBudget($id: ID!) {
16
+ planmagnetBudget(id: $id) {
17
+ id
18
+ name
19
+ description
20
+ status
21
+ plannedAmount
22
+ spentAmount
23
+ currency
24
+ periodStart
25
+ periodEnd
26
+ approverId
27
+ approvedAt
28
+ createdBy
29
+ createdAt
30
+ projectId
31
+ project {
32
+ id
33
+ name
34
+ key
35
+ }
36
+ expenses {
37
+ id
38
+ description
39
+ amount
40
+ category
41
+ date
42
+ workItemId
43
+ referenceUrl
44
+ createdBy
45
+ createdAt
46
+ }
47
+ }
48
+ }
49
+ `;
50
+ function w() {
51
+ let m = o("budgetId"), w = e(), { data: T, loading: E, error: D } = h(C, {
52
+ variables: { id: m },
53
+ skip: !m
54
+ });
55
+ if (!m) return /* @__PURE__ */ g("div", {
56
+ className: "p-6 text-sm text-muted-foreground",
57
+ children: "No budget selected. Open one from the list."
58
+ });
59
+ if (E && !T) return /* @__PURE__ */ g("div", {
60
+ className: "flex items-center justify-center p-12",
61
+ children: /* @__PURE__ */ g("div", { className: "animate-spin h-8 w-8 border-2 border-primary border-t-transparent rounded-full" })
62
+ });
63
+ if (D) return /* @__PURE__ */ _("div", {
64
+ className: "p-6 space-y-3",
65
+ children: [/* @__PURE__ */ _("p", {
66
+ role: "alert",
67
+ className: "text-sm text-destructive",
68
+ children: ["The budget could not be loaded: ", D.message]
69
+ }), /* @__PURE__ */ _(v, {
70
+ size: "sm",
71
+ variant: "ghost",
72
+ onClick: () => w("/projects/budgets-page"),
73
+ children: [/* @__PURE__ */ g(f, { className: "mr-1 h-4 w-4" }), "All budgets"]
74
+ })]
75
+ });
76
+ let O = T?.planmagnetBudget;
77
+ if (!O) return /* @__PURE__ */ _("div", {
78
+ className: "p-6 space-y-3",
79
+ children: [/* @__PURE__ */ g("p", {
80
+ className: "text-sm text-muted-foreground",
81
+ children: "This budget does not exist in this workspace, or it was deleted."
82
+ }), /* @__PURE__ */ _(v, {
83
+ size: "sm",
84
+ variant: "ghost",
85
+ onClick: () => w("/projects/budgets-page"),
86
+ children: [/* @__PURE__ */ g(f, { className: "mr-1 h-4 w-4" }), "All budgets"]
87
+ })]
88
+ });
89
+ let k = i(O.plannedAmount, O.spentAmount), A = n[r(k)], j = k === null ? null : Math.round(k * 100), M = O.plannedAmount - O.spentAmount, N = [...O.expenses].sort((e, t) => t.date.localeCompare(e.date)), P = [
90
+ "GetBudget",
91
+ "BudgetsList",
92
+ "GetBudgetPerformance"
93
+ ], F = O.project;
94
+ return /* @__PURE__ */ _("div", {
95
+ className: "space-y-6 p-6",
96
+ children: [
97
+ /* @__PURE__ */ _(v, {
98
+ size: "sm",
99
+ variant: "ghost",
100
+ onClick: () => w("/projects/budgets-page"),
101
+ children: [/* @__PURE__ */ g(f, { className: "mr-1 h-4 w-4" }), "All budgets"]
102
+ }),
103
+ /* @__PURE__ */ _("div", {
104
+ className: "flex flex-wrap items-start justify-between gap-4",
105
+ children: [/* @__PURE__ */ _("div", { children: [
106
+ /* @__PURE__ */ g("h1", {
107
+ className: "text-2xl font-semibold",
108
+ children: O.name
109
+ }),
110
+ /* @__PURE__ */ g("p", {
111
+ className: "text-sm text-muted-foreground mt-1",
112
+ children: O.description ?? "No description"
113
+ }),
114
+ /* @__PURE__ */ g("p", {
115
+ className: "text-xs text-muted-foreground mt-1",
116
+ children: F ? /* @__PURE__ */ _("button", {
117
+ type: "button",
118
+ className: "hover:underline",
119
+ onClick: () => w(`/projects/project-detail-page/${encodeURIComponent(F.id)}`),
120
+ children: [
121
+ F.name,
122
+ " (",
123
+ F.key,
124
+ ")"
125
+ ]
126
+ }) : "No project"
127
+ })
128
+ ] }), /* @__PURE__ */ g(c, { budget: O })]
129
+ }),
130
+ /* @__PURE__ */ _("div", {
131
+ className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
132
+ children: [/* @__PURE__ */ _("div", {
133
+ className: "lg:col-span-2 space-y-6",
134
+ children: [/* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Plan vs actual" }) }), /* @__PURE__ */ _(b, {
135
+ className: "space-y-3",
136
+ children: [
137
+ /* @__PURE__ */ _("div", {
138
+ className: "grid grid-cols-3 gap-4 text-center",
139
+ children: [
140
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
141
+ className: "text-lg font-semibold tabular-nums",
142
+ children: a(O.plannedAmount, O.currency)
143
+ }), /* @__PURE__ */ g("p", {
144
+ className: "text-xs text-muted-foreground",
145
+ children: "Planned"
146
+ })] }),
147
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
148
+ className: "text-lg font-semibold tabular-nums",
149
+ children: a(O.spentAmount, O.currency)
150
+ }), /* @__PURE__ */ g("p", {
151
+ className: "text-xs text-muted-foreground",
152
+ children: "Spent"
153
+ })] }),
154
+ /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("p", {
155
+ className: `text-lg font-semibold tabular-nums ${M < 0 ? "text-status-error-text" : ""}`,
156
+ children: a(M, O.currency)
157
+ }), /* @__PURE__ */ g("p", {
158
+ className: "text-xs text-muted-foreground",
159
+ children: "Remaining"
160
+ })] })
161
+ ]
162
+ }),
163
+ /* @__PURE__ */ g("div", {
164
+ role: "meter",
165
+ "aria-valuemin": 0,
166
+ "aria-valuemax": Math.max(100, j ?? 0),
167
+ "aria-valuenow": j ?? 0,
168
+ "aria-label": `${j === null ? "no plan" : `${j}%`} spent`,
169
+ className: `h-3 w-full overflow-hidden rounded-full ${A.track}`,
170
+ children: /* @__PURE__ */ g("div", {
171
+ className: `h-full rounded-full ${A.fill}`,
172
+ style: { width: `${Math.min(j ?? 0, 100)}%` }
173
+ })
174
+ }),
175
+ /* @__PURE__ */ _("span", {
176
+ className: `flex items-center gap-1 text-xs ${A.text}`,
177
+ children: [/* @__PURE__ */ g(A.icon, {
178
+ className: "h-3 w-3",
179
+ "aria-hidden": !0
180
+ }), j === null ? "No plan set" : `${j}% of plan spent`]
181
+ })
182
+ ]
183
+ })] }), /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ _(x, {
184
+ className: "flex flex-row items-center justify-between space-y-0",
185
+ children: [/* @__PURE__ */ _(S, {
186
+ className: "flex items-center gap-2",
187
+ children: [
188
+ /* @__PURE__ */ g(p, { className: "h-4 w-4" }),
189
+ "Expenses (",
190
+ N.length,
191
+ ")"
192
+ ]
193
+ }), /* @__PURE__ */ g(d, {
194
+ budgetId: O.id,
195
+ projectId: O.projectId,
196
+ refetchQueries: P
197
+ })]
198
+ }), /* @__PURE__ */ _(b, {
199
+ className: "space-y-2",
200
+ children: [N.length === 0 && /* @__PURE__ */ g("p", {
201
+ className: "text-sm text-muted-foreground text-center py-4",
202
+ children: "No expenses logged yet."
203
+ }), N.map((e) => /* @__PURE__ */ _("div", {
204
+ className: "flex items-start justify-between gap-3 rounded-md border p-3",
205
+ children: [/* @__PURE__ */ _("div", {
206
+ className: "min-w-0 space-y-1",
207
+ children: [
208
+ /* @__PURE__ */ g("p", {
209
+ className: "text-sm font-medium truncate",
210
+ children: e.description
211
+ }),
212
+ /* @__PURE__ */ _("p", {
213
+ className: "text-xs text-muted-foreground",
214
+ children: [
215
+ new Date(e.date).toLocaleDateString(),
216
+ e.category ? ` · ${e.category}` : "",
217
+ " ·",
218
+ " ",
219
+ /* @__PURE__ */ g(t, { id: e.createdBy })
220
+ ]
221
+ }),
222
+ e.referenceUrl && /* @__PURE__ */ g("a", {
223
+ href: e.referenceUrl,
224
+ target: "_blank",
225
+ rel: "noreferrer",
226
+ className: "text-xs text-primary hover:underline",
227
+ children: "Receipt / reference"
228
+ })
229
+ ]
230
+ }), /* @__PURE__ */ _("div", {
231
+ className: "flex shrink-0 items-center gap-2",
232
+ children: [
233
+ /* @__PURE__ */ g("span", {
234
+ className: "text-sm font-semibold tabular-nums",
235
+ children: a(e.amount, O.currency)
236
+ }),
237
+ /* @__PURE__ */ g(u, {
238
+ expense: e,
239
+ refetchQueries: P
240
+ }),
241
+ /* @__PURE__ */ g(l, {
242
+ id: e.id,
243
+ description: e.description,
244
+ refetchQueries: P
245
+ })
246
+ ]
247
+ })]
248
+ }, e.id))]
249
+ })] })]
250
+ }), /* @__PURE__ */ g("div", {
251
+ className: "space-y-6",
252
+ children: /* @__PURE__ */ _(y, { children: [/* @__PURE__ */ g(x, { children: /* @__PURE__ */ g(S, { children: "Details" }) }), /* @__PURE__ */ _(b, {
253
+ className: "space-y-2 text-sm",
254
+ children: [
255
+ /* @__PURE__ */ _("div", {
256
+ className: "flex justify-between",
257
+ children: [/* @__PURE__ */ g("span", {
258
+ className: "text-muted-foreground",
259
+ children: "Status"
260
+ }), /* @__PURE__ */ g("span", { children: s(O.status) })]
261
+ }),
262
+ /* @__PURE__ */ _("div", {
263
+ className: "flex justify-between",
264
+ children: [/* @__PURE__ */ g("span", {
265
+ className: "text-muted-foreground",
266
+ children: "Currency"
267
+ }), /* @__PURE__ */ g("span", {
268
+ className: "font-mono",
269
+ children: O.currency
270
+ })]
271
+ }),
272
+ /* @__PURE__ */ _("div", {
273
+ className: "flex justify-between",
274
+ children: [/* @__PURE__ */ g("span", {
275
+ className: "text-muted-foreground",
276
+ children: "Period"
277
+ }), /* @__PURE__ */ _("span", { children: [
278
+ O.periodStart ? new Date(O.periodStart).toLocaleDateString() : "—",
279
+ " –",
280
+ " ",
281
+ O.periodEnd ? new Date(O.periodEnd).toLocaleDateString() : "—"
282
+ ] })]
283
+ }),
284
+ /* @__PURE__ */ _("div", {
285
+ className: "flex justify-between",
286
+ children: [/* @__PURE__ */ g("span", {
287
+ className: "text-muted-foreground",
288
+ children: "Created by"
289
+ }), /* @__PURE__ */ g(t, { id: O.createdBy })]
290
+ }),
291
+ O.approverId && /* @__PURE__ */ _("div", {
292
+ className: "flex justify-between",
293
+ children: [/* @__PURE__ */ g("span", {
294
+ className: "text-muted-foreground",
295
+ children: "Approved by"
296
+ }), /* @__PURE__ */ g(t, { id: O.approverId })]
297
+ }),
298
+ O.approvedAt && /* @__PURE__ */ _("div", {
299
+ className: "flex justify-between",
300
+ children: [/* @__PURE__ */ g("span", {
301
+ className: "text-muted-foreground",
302
+ children: "Approved"
303
+ }), /* @__PURE__ */ g("span", { children: new Date(O.approvedAt).toLocaleDateString() })]
304
+ })
305
+ ]
306
+ })] })
307
+ })]
308
+ })
309
+ ]
310
+ });
311
+ }
312
+ //#endregion
313
+ export { w as ProjectsBudgetDetailPagePage };
314
+
315
+ //# sourceMappingURL=ProjectsBudgetDetailPagePage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectsBudgetDetailPagePage.js","names":[],"sources":["../../../src/planmagnet/pages/ProjectsBudgetDetailPagePage.tsx"],"sourcesContent":["import { gql } from '@apollo/client';\nimport { useQuery } from '@apollo/client/react';\nimport { Button, Card, CardContent, CardHeader, CardTitle } from '@burdenoff/fe-libs/ui';\nimport { ArrowLeft, Receipt } from 'lucide-react';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { BudgetHeaderActions } from '../budgets/BudgetControls';\nimport {\n LogExpenseDialog,\n EditExpenseDialog,\n DeleteExpenseButton,\n} from '../budgets/ExpenseControls';\nimport { budgetState, budgetUsage, BUDGET_STATE_STYLE, money } from '../budgets/budgetMath';\nimport { PersonName } from '../people/People';\nimport { useEntityId } from '../hooks/useEntityId';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\n\n/** One budget (BOFF-8047): its plan vs spend, and every expense logged against it. */\n\nconst GET_BUDGET = gql`\n query GetBudget($id: ID!) {\n planmagnetBudget(id: $id) {\n id\n name\n description\n status\n plannedAmount\n spentAmount\n currency\n periodStart\n periodEnd\n approverId\n approvedAt\n createdBy\n createdAt\n projectId\n project {\n id\n name\n key\n }\n expenses {\n id\n description\n amount\n category\n date\n workItemId\n referenceUrl\n createdBy\n createdAt\n }\n }\n }\n`;\n\ninterface ExpenseNode {\n id: string;\n description: string;\n amount: number;\n category: string | null;\n date: string;\n workItemId: string | null;\n referenceUrl: string | null;\n createdBy: string;\n createdAt: string;\n}\n\ninterface BudgetNode {\n id: string;\n name: string;\n description: string | null;\n status: string;\n plannedAmount: number;\n spentAmount: number;\n currency: string;\n periodStart: string | null;\n periodEnd: string | null;\n approverId: string | null;\n approvedAt: string | null;\n createdBy: string;\n createdAt: string;\n projectId: string;\n project: { id: string; name: string; key: string } | null;\n expenses: ExpenseNode[];\n}\n\nexport function ProjectsBudgetDetailPagePage() {\n const budgetId = useEntityId('budgetId');\n const navigate = usePlanMagnetNavigate();\n\n const { data, loading, error } = useQuery<{ planmagnetBudget: BudgetNode | null }>(GET_BUDGET, {\n variables: { id: budgetId },\n skip: !budgetId,\n });\n\n if (!budgetId) {\n return (\n <div className=\"p-6 text-sm text-muted-foreground\">\n No budget selected. Open one from the list.\n </div>\n );\n }\n // Only before the first load: a refetch after a write must not blank the page.\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 }\n if (error) {\n return (\n <div className=\"p-6 space-y-3\">\n <p role=\"alert\" className=\"text-sm text-destructive\">\n The budget could not be loaded: {error.message}\n </p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/projects/budgets-page')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" />\n All budgets\n </Button>\n </div>\n );\n }\n const budget = data?.planmagnetBudget;\n if (!budget) {\n return (\n <div className=\"p-6 space-y-3\">\n <p className=\"text-sm text-muted-foreground\">\n This budget does not exist in this workspace, or it was deleted.\n </p>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/projects/budgets-page')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" />\n All budgets\n </Button>\n </div>\n );\n }\n\n const usage = budgetUsage(budget.plannedAmount, budget.spentAmount);\n const state = budgetState(usage);\n const style = BUDGET_STATE_STYLE[state];\n const percent = usage === null ? null : Math.round(usage * 100);\n const remaining = budget.plannedAmount - budget.spentAmount;\n const expenses = [...budget.expenses].sort((a, b) => b.date.localeCompare(a.date));\n const refetchQueries = ['GetBudget', 'BudgetsList', 'GetBudgetPerformance'];\n // A local const so the null-check narrows through the closure below (a nested arrow reading\n // `budget.project` directly would not stay narrowed, forcing a non-null assertion instead).\n const project = budget.project;\n\n return (\n <div className=\"space-y-6 p-6\">\n <Button size=\"sm\" variant=\"ghost\" onClick={() => navigate('/projects/budgets-page')}>\n <ArrowLeft className=\"mr-1 h-4 w-4\" />\n All budgets\n </Button>\n\n <div className=\"flex flex-wrap items-start justify-between gap-4\">\n <div>\n <h1 className=\"text-2xl font-semibold\">{budget.name}</h1>\n <p className=\"text-sm text-muted-foreground mt-1\">\n {budget.description ?? 'No description'}\n </p>\n <p className=\"text-xs text-muted-foreground mt-1\">\n {project ? (\n <button\n type=\"button\"\n className=\"hover:underline\"\n onClick={() =>\n navigate(`/projects/project-detail-page/${encodeURIComponent(project.id)}`)\n }\n >\n {project.name} ({project.key})\n </button>\n ) : (\n 'No project'\n )}\n </p>\n </div>\n <BudgetHeaderActions budget={budget} />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Plan vs actual</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n <div className=\"grid grid-cols-3 gap-4 text-center\">\n <div>\n <p className=\"text-lg font-semibold tabular-nums\">\n {money(budget.plannedAmount, budget.currency)}\n </p>\n <p className=\"text-xs text-muted-foreground\">Planned</p>\n </div>\n <div>\n <p className=\"text-lg font-semibold tabular-nums\">\n {money(budget.spentAmount, budget.currency)}\n </p>\n <p className=\"text-xs text-muted-foreground\">Spent</p>\n </div>\n <div>\n <p\n className={`text-lg font-semibold tabular-nums ${remaining < 0 ? 'text-status-error-text' : ''}`}\n >\n {money(remaining, budget.currency)}\n </p>\n <p className=\"text-xs text-muted-foreground\">Remaining</p>\n </div>\n </div>\n <div\n role=\"meter\"\n aria-valuemin={0}\n aria-valuemax={Math.max(100, percent ?? 0)}\n aria-valuenow={percent ?? 0}\n aria-label={`${percent === null ? 'no plan' : `${percent}%`} spent`}\n className={`h-3 w-full overflow-hidden rounded-full ${style.track}`}\n >\n <div\n className={`h-full rounded-full ${style.fill}`}\n style={{ width: `${Math.min(percent ?? 0, 100)}%` }}\n />\n </div>\n <span className={`flex items-center gap-1 text-xs ${style.text}`}>\n <style.icon className=\"h-3 w-3\" aria-hidden={true} />\n {percent === null ? 'No plan set' : `${percent}% of plan spent`}\n </span>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0\">\n <CardTitle className=\"flex items-center gap-2\">\n <Receipt className=\"h-4 w-4\" />\n Expenses ({expenses.length})\n </CardTitle>\n <LogExpenseDialog\n budgetId={budget.id}\n projectId={budget.projectId}\n refetchQueries={refetchQueries}\n />\n </CardHeader>\n <CardContent className=\"space-y-2\">\n {expenses.length === 0 && (\n <p className=\"text-sm text-muted-foreground text-center py-4\">\n No expenses logged yet.\n </p>\n )}\n {expenses.map((expense) => (\n <div\n key={expense.id}\n className=\"flex items-start justify-between gap-3 rounded-md border p-3\"\n >\n <div className=\"min-w-0 space-y-1\">\n <p className=\"text-sm font-medium truncate\">{expense.description}</p>\n <p className=\"text-xs text-muted-foreground\">\n {new Date(expense.date).toLocaleDateString()}\n {expense.category ? ` · ${expense.category}` : ''} ·{' '}\n <PersonName id={expense.createdBy} />\n </p>\n {expense.referenceUrl && (\n <a\n href={expense.referenceUrl}\n target=\"_blank\"\n rel=\"noreferrer\"\n className=\"text-xs text-primary hover:underline\"\n >\n Receipt / reference\n </a>\n )}\n </div>\n <div className=\"flex shrink-0 items-center gap-2\">\n <span className=\"text-sm font-semibold tabular-nums\">\n {money(expense.amount, budget.currency)}\n </span>\n <EditExpenseDialog expense={expense} refetchQueries={refetchQueries} />\n <DeleteExpenseButton\n id={expense.id}\n description={expense.description}\n refetchQueries={refetchQueries}\n />\n </div>\n </div>\n ))}\n </CardContent>\n </Card>\n </div>\n\n <div className=\"space-y-6\">\n <Card>\n <CardHeader>\n <CardTitle>Details</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-2 text-sm\">\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Status</span>\n <span>{enumLabel(budget.status)}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Currency</span>\n <span className=\"font-mono\">{budget.currency}</span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Period</span>\n <span>\n {budget.periodStart ? new Date(budget.periodStart).toLocaleDateString() : '—'} –{' '}\n {budget.periodEnd ? new Date(budget.periodEnd).toLocaleDateString() : '—'}\n </span>\n </div>\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Created by</span>\n <PersonName id={budget.createdBy} />\n </div>\n {budget.approverId && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Approved by</span>\n <PersonName id={budget.approverId} />\n </div>\n )}\n {budget.approvedAt && (\n <div className=\"flex justify-between\">\n <span className=\"text-muted-foreground\">Approved</span>\n <span>{new Date(budget.approvedAt).toLocaleDateString()}</span>\n </div>\n )}\n </CardContent>\n </Card>\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function ProjectsBudgetsPagePage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ProjectsBudgetsPagePage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectsBudgetsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsBudgetsPagePage.tsx"],"names":[],"mappings":"AA8DA,wBAAgB,uBAAuB,4CAmKtC"}