@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
@@ -0,0 +1,470 @@
1
+ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
+ import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
3
+ import { ConfirmButton as r, ErrorLine as i } from "../controls/ConfirmButton.js";
4
+ import { ProjectSelect as a } from "../controls/Pickers.js";
5
+ import { enumLabel as o } from "../controls/WorkItemControls.js";
6
+ import { toDateInputValue as s } from "../time/timeShared.js";
7
+ import { useState as c } from "react";
8
+ import { CheckCircle2 as l, Pencil as u, Plus as d, Trash2 as f } from "lucide-react";
9
+ import { gql as p } from "@apollo/client";
10
+ import { useMutation as m } from "@apollo/client/react";
11
+ import { jsx as h, jsxs as g } from "react/jsx-runtime";
12
+ import { Button as _, Input as v, ResponsiveDialog as y, ResponsiveDialogContent as b, ResponsiveDialogDescription as x, ResponsiveDialogFooter as S, ResponsiveDialogHeader as C, ResponsiveDialogTitle as w, ResponsiveDialogTrigger as T } from "@burdenoff/fe-libs/ui";
13
+ //#region src/planmagnet/budgets/BudgetControls.tsx
14
+ var E = [
15
+ "DRAFT",
16
+ "ACTIVE",
17
+ "APPROVED",
18
+ "EXCEEDED",
19
+ "CLOSED"
20
+ ], D = E.filter((e) => e !== "APPROVED"), O = [
21
+ "BudgetsList",
22
+ "GetBudget",
23
+ "GetBudgetPerformance",
24
+ "ProjectBudgetsSummary"
25
+ ], k = p`
26
+ mutation BudgetControlsCreate($input: PlanmagnetCreateBudgetInput!) {
27
+ planmagnetCreateBudget(input: $input) {
28
+ id
29
+ name
30
+ status
31
+ plannedAmount
32
+ currency
33
+ createdAt
34
+ }
35
+ }
36
+ `, A = p`
37
+ mutation BudgetControlsUpdate($id: ID!, $input: PlanmagnetUpdateBudgetInput!) {
38
+ planmagnetUpdateBudget(id: $id, input: $input) {
39
+ id
40
+ name
41
+ description
42
+ status
43
+ plannedAmount
44
+ currency
45
+ periodStart
46
+ periodEnd
47
+ updatedAt
48
+ }
49
+ }
50
+ `, j = p`
51
+ mutation BudgetControlsApprove($id: ID!) {
52
+ planmagnetApproveBudget(id: $id) {
53
+ id
54
+ status
55
+ approverId
56
+ approvedAt
57
+ updatedAt
58
+ }
59
+ }
60
+ `, M = p`
61
+ mutation BudgetControlsDelete($id: ID!) {
62
+ planmagnetDeleteBudget(id: $id)
63
+ }
64
+ `, N = "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60", P = (e, t) => e instanceof Error ? e.message : t;
65
+ function F({ presetProjectId: e, onCreated: t, triggerSize: r = "sm" }) {
66
+ let o = {
67
+ projectId: e ?? "",
68
+ name: "",
69
+ description: "",
70
+ plannedAmount: "",
71
+ currency: "USD",
72
+ periodStart: "",
73
+ periodEnd: ""
74
+ }, [s, l] = c(!1), [u, f] = c(o), [p, E] = c(null), [D, { loading: A }] = m(k, {
75
+ refetchQueries: O,
76
+ awaitRefetchQueries: !0
77
+ }), j = u.periodStart && u.periodEnd && u.periodEnd < u.periodStart, M = Number(u.plannedAmount), F = !A && u.name.trim() !== "" && !!u.projectId && Number.isFinite(M) && M >= 0 && u.currency.trim() !== "" && !j, I = async () => {
78
+ E(null);
79
+ try {
80
+ let e = (await n(D({ variables: { input: {
81
+ projectId: u.projectId,
82
+ name: u.name.trim(),
83
+ ...u.description.trim() ? { description: u.description.trim() } : {},
84
+ plannedAmount: M,
85
+ currency: u.currency.trim().toUpperCase(),
86
+ ...u.periodStart ? { periodStart: u.periodStart } : {},
87
+ ...u.periodEnd ? { periodEnd: u.periodEnd } : {}
88
+ } } }))).data?.planmagnetCreateBudget.id;
89
+ l(!1), f(o), e && t?.(e);
90
+ } catch (e) {
91
+ E(P(e, "The budget could not be created."));
92
+ }
93
+ };
94
+ return /* @__PURE__ */ g(y, {
95
+ open: s,
96
+ onOpenChange: (e) => {
97
+ l(e), e ? f(o) : E(null);
98
+ },
99
+ children: [/* @__PURE__ */ h(T, {
100
+ asChild: !0,
101
+ children: /* @__PURE__ */ g(_, {
102
+ size: r,
103
+ "data-testid": "new-budget-open",
104
+ children: [/* @__PURE__ */ h(d, { className: "mr-1 h-4 w-4" }), "New budget"]
105
+ })
106
+ }), /* @__PURE__ */ g(b, {
107
+ className: "max-w-lg",
108
+ children: [
109
+ /* @__PURE__ */ g(C, { children: [/* @__PURE__ */ h(w, { children: "New budget" }), /* @__PURE__ */ h(x, { children: "The budget starts in Draft." })] }),
110
+ /* @__PURE__ */ g("div", {
111
+ className: "space-y-3",
112
+ children: [
113
+ !e && /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
114
+ htmlFor: "new-budget-project",
115
+ className: "text-sm font-medium",
116
+ children: "Project"
117
+ }), /* @__PURE__ */ h("div", {
118
+ className: "mt-1",
119
+ children: /* @__PURE__ */ h(a, {
120
+ id: "new-budget-project",
121
+ value: u.projectId,
122
+ onChange: (e) => f({
123
+ ...u,
124
+ projectId: e
125
+ })
126
+ })
127
+ })] }),
128
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
129
+ htmlFor: "new-budget-name",
130
+ className: "text-sm font-medium",
131
+ children: "Name"
132
+ }), /* @__PURE__ */ h(v, {
133
+ id: "new-budget-name",
134
+ className: "mt-1",
135
+ value: u.name,
136
+ onChange: (e) => f({
137
+ ...u,
138
+ name: e.target.value
139
+ })
140
+ })] }),
141
+ /* @__PURE__ */ g("div", {
142
+ className: "grid grid-cols-2 gap-3",
143
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
144
+ htmlFor: "new-budget-amount",
145
+ className: "text-sm font-medium",
146
+ children: "Planned amount"
147
+ }), /* @__PURE__ */ h(v, {
148
+ id: "new-budget-amount",
149
+ className: "mt-1",
150
+ type: "number",
151
+ min: "0",
152
+ step: "0.01",
153
+ inputMode: "decimal",
154
+ value: u.plannedAmount,
155
+ onChange: (e) => f({
156
+ ...u,
157
+ plannedAmount: e.target.value
158
+ })
159
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
160
+ htmlFor: "new-budget-currency",
161
+ className: "text-sm font-medium",
162
+ children: "Currency"
163
+ }), /* @__PURE__ */ h(v, {
164
+ id: "new-budget-currency",
165
+ className: "mt-1",
166
+ placeholder: "USD",
167
+ maxLength: 3,
168
+ value: u.currency,
169
+ onChange: (e) => f({
170
+ ...u,
171
+ currency: e.target.value.toUpperCase()
172
+ })
173
+ })] })]
174
+ }),
175
+ /* @__PURE__ */ g("div", {
176
+ className: "grid grid-cols-2 gap-3",
177
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
178
+ htmlFor: "new-budget-start",
179
+ className: "text-sm font-medium",
180
+ children: "Period start (optional)"
181
+ }), /* @__PURE__ */ h(v, {
182
+ id: "new-budget-start",
183
+ className: "mt-1",
184
+ type: "date",
185
+ value: u.periodStart,
186
+ onChange: (e) => f({
187
+ ...u,
188
+ periodStart: e.target.value
189
+ })
190
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
191
+ htmlFor: "new-budget-end",
192
+ className: "text-sm font-medium",
193
+ children: "Period end (optional)"
194
+ }), /* @__PURE__ */ h(v, {
195
+ id: "new-budget-end",
196
+ className: "mt-1",
197
+ type: "date",
198
+ value: u.periodEnd,
199
+ onChange: (e) => f({
200
+ ...u,
201
+ periodEnd: e.target.value
202
+ })
203
+ })] })]
204
+ }),
205
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
206
+ htmlFor: "new-budget-description",
207
+ className: "text-sm font-medium",
208
+ children: "Description"
209
+ }), /* @__PURE__ */ h("textarea", {
210
+ id: "new-budget-description",
211
+ className: `${N} min-h-[64px]`,
212
+ value: u.description,
213
+ onChange: (e) => f({
214
+ ...u,
215
+ description: e.target.value
216
+ })
217
+ })] }),
218
+ j && /* @__PURE__ */ h(i, { message: "The period end cannot be before the period start." }),
219
+ p && /* @__PURE__ */ h(i, { message: p })
220
+ ]
221
+ }),
222
+ /* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(_, {
223
+ variant: "outline",
224
+ onClick: () => l(!1),
225
+ children: "Cancel"
226
+ }), /* @__PURE__ */ h(_, {
227
+ onClick: () => void I(),
228
+ disabled: !F,
229
+ "data-testid": "new-budget-submit",
230
+ children: A ? "Creating…" : "Create budget"
231
+ })] })
232
+ ]
233
+ })]
234
+ });
235
+ }
236
+ function I({ budget: a }) {
237
+ let s = e(), [u, d] = m(A, {
238
+ refetchQueries: O,
239
+ awaitRefetchQueries: !0
240
+ }), [p, v] = m(j, {
241
+ refetchQueries: O,
242
+ awaitRefetchQueries: !0
243
+ }), [y, b] = m(M, {
244
+ refetchQueries: ["BudgetsList", "ProjectBudgetsSummary"],
245
+ context: t
246
+ }), [x, S] = c(null), C = d.loading || v.loading || b.loading, w = D.includes(a.status) ? D : [a.status, ...D], T = async (e) => {
247
+ S(null);
248
+ try {
249
+ await n(u({ variables: {
250
+ id: a.id,
251
+ input: { status: e }
252
+ } }));
253
+ } catch (e) {
254
+ S(P(e, "The status could not be changed."));
255
+ }
256
+ }, E = async () => {
257
+ S(null);
258
+ try {
259
+ await n(p({ variables: { id: a.id } }));
260
+ } catch (e) {
261
+ S(P(e, "The budget could not be approved."));
262
+ }
263
+ }, k = async () => {
264
+ S(null);
265
+ try {
266
+ await n(y({ variables: { id: a.id } })), s("/projects/budgets-page");
267
+ } catch (e) {
268
+ S(P(e, "The budget could not be deleted."));
269
+ }
270
+ };
271
+ return /* @__PURE__ */ g("div", {
272
+ className: "flex flex-col items-end gap-2",
273
+ children: [/* @__PURE__ */ g("div", {
274
+ className: "flex flex-wrap items-center justify-end gap-2",
275
+ children: [
276
+ /* @__PURE__ */ h("select", {
277
+ "aria-label": "Status",
278
+ "data-testid": "budget-status",
279
+ className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
280
+ value: a.status,
281
+ disabled: C,
282
+ onChange: (e) => {
283
+ e.target.value !== a.status && T(e.target.value);
284
+ },
285
+ children: w.map((e) => /* @__PURE__ */ h("option", {
286
+ value: e,
287
+ children: o(e)
288
+ }, e))
289
+ }),
290
+ a.status !== "APPROVED" && a.status !== "CLOSED" && /* @__PURE__ */ g(_, {
291
+ size: "sm",
292
+ variant: "outline",
293
+ disabled: C,
294
+ onClick: () => void E(),
295
+ children: [/* @__PURE__ */ h(l, { className: "mr-1 h-4 w-4" }), "Approve"]
296
+ }),
297
+ /* @__PURE__ */ h(L, { budget: a }),
298
+ /* @__PURE__ */ h(r, {
299
+ label: "Delete",
300
+ variant: "outline",
301
+ icon: /* @__PURE__ */ h(f, { className: "mr-1 h-4 w-4" }),
302
+ title: `Delete "${a.name}"?`,
303
+ description: "The budget and its logged expenses are removed. This cannot be undone here.",
304
+ confirmLabel: "Delete budget",
305
+ destructive: !0,
306
+ disabled: C,
307
+ onConfirm: () => void k()
308
+ })
309
+ ]
310
+ }), x && /* @__PURE__ */ h(i, { message: x })]
311
+ });
312
+ }
313
+ function L({ budget: e }) {
314
+ let t = {
315
+ name: e.name,
316
+ description: e.description ?? "",
317
+ plannedAmount: String(e.plannedAmount),
318
+ currency: e.currency,
319
+ periodStart: s(e.periodStart),
320
+ periodEnd: s(e.periodEnd)
321
+ }, [r, a] = c(!1), [o, l] = c(t), [d, f] = c(null), [p, E] = m(A, {
322
+ refetchQueries: O,
323
+ awaitRefetchQueries: !0
324
+ }), D = Number(o.plannedAmount), k = o.periodStart && o.periodEnd && o.periodEnd < o.periodStart, j = !E.loading && o.name.trim() !== "" && Number.isFinite(D) && D >= 0 && !k, M = async () => {
325
+ f(null);
326
+ let r = {};
327
+ if (o.name.trim() !== t.name && (r.name = o.name.trim()), o.description.trim() !== t.description.trim() && (r.description = o.description.trim() || null), D !== e.plannedAmount && (r.plannedAmount = D), o.currency.trim().toUpperCase() !== t.currency && (r.currency = o.currency.trim().toUpperCase()), o.periodStart !== t.periodStart && (r.periodStart = o.periodStart || null), o.periodEnd !== t.periodEnd && (r.periodEnd = o.periodEnd || null), Object.keys(r).length === 0) {
328
+ a(!1);
329
+ return;
330
+ }
331
+ try {
332
+ await n(p({ variables: {
333
+ id: e.id,
334
+ input: r
335
+ } })), a(!1);
336
+ } catch (e) {
337
+ f(P(e, "The budget could not be saved."));
338
+ }
339
+ };
340
+ return /* @__PURE__ */ g(y, {
341
+ open: r,
342
+ onOpenChange: (e) => {
343
+ a(e), e ? l(t) : f(null);
344
+ },
345
+ children: [/* @__PURE__ */ h(T, {
346
+ asChild: !0,
347
+ children: /* @__PURE__ */ g(_, {
348
+ size: "sm",
349
+ variant: "outline",
350
+ "data-testid": "budget-edit",
351
+ children: [/* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Edit"]
352
+ })
353
+ }), /* @__PURE__ */ g(b, {
354
+ className: "max-w-lg",
355
+ children: [
356
+ /* @__PURE__ */ g(C, { children: [/* @__PURE__ */ h(w, { children: "Edit budget" }), /* @__PURE__ */ h(x, { children: "Change the name, amount, currency, period or description." })] }),
357
+ /* @__PURE__ */ g("div", {
358
+ className: "space-y-3",
359
+ children: [
360
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
361
+ htmlFor: "edit-budget-name",
362
+ className: "text-sm font-medium",
363
+ children: "Name"
364
+ }), /* @__PURE__ */ h(v, {
365
+ id: "edit-budget-name",
366
+ className: "mt-1",
367
+ value: o.name,
368
+ onChange: (e) => l({
369
+ ...o,
370
+ name: e.target.value
371
+ })
372
+ })] }),
373
+ /* @__PURE__ */ g("div", {
374
+ className: "grid grid-cols-2 gap-3",
375
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
376
+ htmlFor: "edit-budget-amount",
377
+ className: "text-sm font-medium",
378
+ children: "Planned amount"
379
+ }), /* @__PURE__ */ h(v, {
380
+ id: "edit-budget-amount",
381
+ className: "mt-1",
382
+ type: "number",
383
+ min: "0",
384
+ step: "0.01",
385
+ inputMode: "decimal",
386
+ value: o.plannedAmount,
387
+ onChange: (e) => l({
388
+ ...o,
389
+ plannedAmount: e.target.value
390
+ })
391
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
392
+ htmlFor: "edit-budget-currency",
393
+ className: "text-sm font-medium",
394
+ children: "Currency"
395
+ }), /* @__PURE__ */ h(v, {
396
+ id: "edit-budget-currency",
397
+ className: "mt-1",
398
+ maxLength: 3,
399
+ value: o.currency,
400
+ onChange: (e) => l({
401
+ ...o,
402
+ currency: e.target.value.toUpperCase()
403
+ })
404
+ })] })]
405
+ }),
406
+ /* @__PURE__ */ g("div", {
407
+ className: "grid grid-cols-2 gap-3",
408
+ children: [/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
409
+ htmlFor: "edit-budget-start",
410
+ className: "text-sm font-medium",
411
+ children: "Period start"
412
+ }), /* @__PURE__ */ h(v, {
413
+ id: "edit-budget-start",
414
+ className: "mt-1",
415
+ type: "date",
416
+ value: o.periodStart,
417
+ onChange: (e) => l({
418
+ ...o,
419
+ periodStart: e.target.value
420
+ })
421
+ })] }), /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
422
+ htmlFor: "edit-budget-end",
423
+ className: "text-sm font-medium",
424
+ children: "Period end"
425
+ }), /* @__PURE__ */ h(v, {
426
+ id: "edit-budget-end",
427
+ className: "mt-1",
428
+ type: "date",
429
+ value: o.periodEnd,
430
+ onChange: (e) => l({
431
+ ...o,
432
+ periodEnd: e.target.value
433
+ })
434
+ })] })]
435
+ }),
436
+ /* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("label", {
437
+ htmlFor: "edit-budget-description",
438
+ className: "text-sm font-medium",
439
+ children: "Description"
440
+ }), /* @__PURE__ */ h("textarea", {
441
+ id: "edit-budget-description",
442
+ className: `${N} min-h-[64px]`,
443
+ value: o.description,
444
+ onChange: (e) => l({
445
+ ...o,
446
+ description: e.target.value
447
+ })
448
+ })] }),
449
+ k && /* @__PURE__ */ h(i, { message: "The period end cannot be before the period start." }),
450
+ d && /* @__PURE__ */ h(i, { message: d })
451
+ ]
452
+ }),
453
+ /* @__PURE__ */ g(S, { children: [/* @__PURE__ */ h(_, {
454
+ variant: "outline",
455
+ onClick: () => a(!1),
456
+ children: "Cancel"
457
+ }), /* @__PURE__ */ h(_, {
458
+ onClick: () => void M(),
459
+ disabled: !j,
460
+ "data-testid": "edit-budget-save",
461
+ children: E.loading ? "Saving…" : "Save"
462
+ })] })
463
+ ]
464
+ })]
465
+ });
466
+ }
467
+ //#endregion
468
+ export { E as BUDGET_STATUSES, I as BudgetHeaderActions, F as CreateBudgetDialog };
469
+
470
+ //# sourceMappingURL=BudgetControls.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BudgetControls.js","names":[],"sources":["../../../src/planmagnet/budgets/BudgetControls.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { gql } from '@apollo/client';\nimport { useMutation } from '@apollo/client/react';\nimport {\n Button,\n Input,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { CheckCircle2, Pencil, Plus, Trash2 } from 'lucide-react';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { ProjectSelect } from '../controls/Pickers';\nimport { throwOnError, LEAVES_PAGE_CONTEXT } from '../utils/mutationResult';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { usePlanMagnetNavigate } from '../providers/PlanMagnetProvider';\nimport { toDateInputValue } from '../time/timeShared';\n\n/**\n * Budget controls (BOFF-8047): create a budget, edit it, approve it, delete it.\n *\n * Approval is its own mutation (`planmagnetApproveBudget`), not a plain status change: it is the only write\n * that records who approved a budget and when (`approverId`/`approvedAt` — the service's `approve()`, unlike\n * `update()`, sets both). The status select below therefore never offers APPROVED as a target; moving to it\n * goes through the Approve button so that audit trail is never silently skipped.\n */\n\nexport const BUDGET_STATUSES = ['DRAFT', 'ACTIVE', 'APPROVED', 'EXCEEDED', 'CLOSED'] as const;\n/** Selectable directly. APPROVED is reached only through the dedicated Approve action (see above). */\nconst SELECTABLE_STATUSES = BUDGET_STATUSES.filter((s) => s !== 'APPROVED');\n\nconst BUDGET_QUERIES = [\n 'BudgetsList',\n 'GetBudget',\n 'GetBudgetPerformance',\n 'ProjectBudgetsSummary',\n];\n\nconst CREATE_BUDGET = gql`\n mutation BudgetControlsCreate($input: PlanmagnetCreateBudgetInput!) {\n planmagnetCreateBudget(input: $input) {\n id\n name\n status\n plannedAmount\n currency\n createdAt\n }\n }\n`;\n\nconst UPDATE_BUDGET = gql`\n mutation BudgetControlsUpdate($id: ID!, $input: PlanmagnetUpdateBudgetInput!) {\n planmagnetUpdateBudget(id: $id, input: $input) {\n id\n name\n description\n status\n plannedAmount\n currency\n periodStart\n periodEnd\n updatedAt\n }\n }\n`;\n\nconst APPROVE_BUDGET = gql`\n mutation BudgetControlsApprove($id: ID!) {\n planmagnetApproveBudget(id: $id) {\n id\n status\n approverId\n approvedAt\n updatedAt\n }\n }\n`;\n\nconst DELETE_BUDGET = gql`\n mutation BudgetControlsDelete($id: ID!) {\n planmagnetDeleteBudget(id: $id)\n }\n`;\n\nconst FIELD =\n 'mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm disabled:opacity-60';\n\nconst messageOf = (e: unknown, fallback: string) => (e instanceof Error ? e.message : fallback);\n\n/** Create a budget. With `presetProjectId` (the Project Detail entry point) the project picker is skipped. */\nexport function CreateBudgetDialog({\n presetProjectId,\n onCreated,\n triggerSize = 'sm',\n}: {\n presetProjectId?: string;\n onCreated?: (id: string) => void;\n triggerSize?: 'sm' | 'default';\n}) {\n const empty = {\n projectId: presetProjectId ?? '',\n name: '',\n description: '',\n plannedAmount: '',\n currency: 'USD',\n periodStart: '',\n periodEnd: '',\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(empty);\n const [error, setError] = useState<string | null>(null);\n const [create, { loading }] = useMutation<{ planmagnetCreateBudget: { id: string } }>(\n CREATE_BUDGET,\n { refetchQueries: BUDGET_QUERIES, awaitRefetchQueries: true }\n );\n\n const invertedLocally = form.periodStart && form.periodEnd && form.periodEnd < form.periodStart;\n const amount = Number(form.plannedAmount);\n const canSubmit =\n !loading &&\n form.name.trim() !== '' &&\n !!form.projectId &&\n Number.isFinite(amount) &&\n amount >= 0 &&\n form.currency.trim() !== '' &&\n !invertedLocally;\n\n const submit = async () => {\n setError(null);\n try {\n const result = await throwOnError(\n create({\n variables: {\n input: {\n projectId: form.projectId,\n name: form.name.trim(),\n ...(form.description.trim() ? { description: form.description.trim() } : {}),\n plannedAmount: amount,\n currency: form.currency.trim().toUpperCase(),\n ...(form.periodStart ? { periodStart: form.periodStart } : {}),\n ...(form.periodEnd ? { periodEnd: form.periodEnd } : {}),\n },\n },\n })\n );\n const id = result.data?.planmagnetCreateBudget.id;\n setOpen(false);\n setForm(empty);\n if (id) onCreated?.(id);\n } catch (e) {\n setError(messageOf(e, 'The budget could not be created.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(empty);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size={triggerSize} data-testid=\"new-budget-open\">\n <Plus className=\"mr-1 h-4 w-4\" />\n New budget\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>New budget</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>The budget starts in Draft.</ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n {!presetProjectId && (\n <div>\n <label htmlFor=\"new-budget-project\" className=\"text-sm font-medium\">\n Project\n </label>\n <div className=\"mt-1\">\n <ProjectSelect\n id=\"new-budget-project\"\n value={form.projectId}\n onChange={(projectId) => setForm({ ...form, projectId })}\n />\n </div>\n </div>\n )}\n <div>\n <label htmlFor=\"new-budget-name\" className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id=\"new-budget-name\"\n className=\"mt-1\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"new-budget-amount\" className=\"text-sm font-medium\">\n Planned amount\n </label>\n <Input\n id=\"new-budget-amount\"\n className=\"mt-1\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n inputMode=\"decimal\"\n value={form.plannedAmount}\n onChange={(e) => setForm({ ...form, plannedAmount: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"new-budget-currency\" className=\"text-sm font-medium\">\n Currency\n </label>\n <Input\n id=\"new-budget-currency\"\n className=\"mt-1\"\n placeholder=\"USD\"\n maxLength={3}\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value.toUpperCase() })}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"new-budget-start\" className=\"text-sm font-medium\">\n Period start (optional)\n </label>\n <Input\n id=\"new-budget-start\"\n className=\"mt-1\"\n type=\"date\"\n value={form.periodStart}\n onChange={(e) => setForm({ ...form, periodStart: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"new-budget-end\" className=\"text-sm font-medium\">\n Period end (optional)\n </label>\n <Input\n id=\"new-budget-end\"\n className=\"mt-1\"\n type=\"date\"\n value={form.periodEnd}\n onChange={(e) => setForm({ ...form, periodEnd: e.target.value })}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"new-budget-description\" className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id=\"new-budget-description\"\n className={`${FIELD} min-h-[64px]`}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {invertedLocally && (\n <ErrorLine message=\"The period end cannot be before the period start.\" />\n )}\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button\n onClick={() => void submit()}\n disabled={!canSubmit}\n data-testid=\"new-budget-submit\"\n >\n {loading ? 'Creating…' : 'Create budget'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n\nexport interface BudgetValues {\n id: string;\n name: string;\n description: string | null;\n status: string;\n plannedAmount: number;\n currency: string;\n periodStart: string | null;\n periodEnd: string | null;\n}\n\n/** Status select, Edit, Approve and Delete for the budget detail header. */\nexport function BudgetHeaderActions({ budget }: { budget: BudgetValues }) {\n const navigate = usePlanMagnetNavigate();\n const [update, updateState] = useMutation(UPDATE_BUDGET, {\n refetchQueries: BUDGET_QUERIES,\n awaitRefetchQueries: true,\n });\n const [approveMutation, approveState] = useMutation(APPROVE_BUDGET, {\n refetchQueries: BUDGET_QUERIES,\n awaitRefetchQueries: true,\n });\n const [remove, removeState] = useMutation(DELETE_BUDGET, {\n refetchQueries: ['BudgetsList', 'ProjectBudgetsSummary'],\n context: LEAVES_PAGE_CONTEXT,\n });\n const [error, setError] = useState<string | null>(null);\n const busy = updateState.loading || approveState.loading || removeState.loading;\n const statusOptions = SELECTABLE_STATUSES.includes(\n budget.status as (typeof SELECTABLE_STATUSES)[number]\n )\n ? SELECTABLE_STATUSES\n : [budget.status, ...SELECTABLE_STATUSES];\n\n const onStatus = async (status: string) => {\n setError(null);\n try {\n await throwOnError(update({ variables: { id: budget.id, input: { status } } }));\n } catch (e) {\n setError(messageOf(e, 'The status could not be changed.'));\n }\n };\n const onApprove = async () => {\n setError(null);\n try {\n await throwOnError(approveMutation({ variables: { id: budget.id } }));\n } catch (e) {\n setError(messageOf(e, 'The budget could not be approved.'));\n }\n };\n const onDelete = async () => {\n setError(null);\n try {\n await throwOnError(remove({ variables: { id: budget.id } }));\n navigate('/projects/budgets-page');\n } catch (e) {\n setError(messageOf(e, 'The budget could not be deleted.'));\n }\n };\n\n return (\n <div className=\"flex flex-col items-end gap-2\">\n <div className=\"flex flex-wrap items-center justify-end gap-2\">\n <select\n aria-label=\"Status\"\n data-testid=\"budget-status\"\n className=\"rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60\"\n value={budget.status}\n disabled={busy}\n onChange={(e) => {\n if (e.target.value !== budget.status) void onStatus(e.target.value);\n }}\n >\n {statusOptions.map((s) => (\n <option key={s} value={s}>\n {enumLabel(s)}\n </option>\n ))}\n </select>\n {budget.status !== 'APPROVED' && budget.status !== 'CLOSED' && (\n <Button size=\"sm\" variant=\"outline\" disabled={busy} onClick={() => void onApprove()}>\n <CheckCircle2 className=\"mr-1 h-4 w-4\" />\n Approve\n </Button>\n )}\n <BudgetEditDialog budget={budget} />\n <ConfirmButton\n label=\"Delete\"\n variant=\"outline\"\n icon={<Trash2 className=\"mr-1 h-4 w-4\" />}\n title={`Delete \"${budget.name}\"?`}\n description=\"The budget and its logged expenses are removed. This cannot be undone here.\"\n confirmLabel=\"Delete budget\"\n destructive\n disabled={busy}\n onConfirm={() => void onDelete()}\n />\n </div>\n {error && <ErrorLine message={error} />}\n </div>\n );\n}\n\n/** Name, description, planned amount, currency and period. Only changed fields are sent. */\nfunction BudgetEditDialog({ budget }: { budget: BudgetValues }) {\n const initial = {\n name: budget.name,\n description: budget.description ?? '',\n plannedAmount: String(budget.plannedAmount),\n currency: budget.currency,\n periodStart: toDateInputValue(budget.periodStart),\n periodEnd: toDateInputValue(budget.periodEnd),\n };\n const [open, setOpen] = useState(false);\n const [form, setForm] = useState(initial);\n const [error, setError] = useState<string | null>(null);\n const [update, state] = useMutation(UPDATE_BUDGET, {\n refetchQueries: BUDGET_QUERIES,\n awaitRefetchQueries: true,\n });\n\n const amount = Number(form.plannedAmount);\n const invertedLocally = form.periodStart && form.periodEnd && form.periodEnd < form.periodStart;\n const canSubmit =\n !state.loading &&\n form.name.trim() !== '' &&\n Number.isFinite(amount) &&\n amount >= 0 &&\n !invertedLocally;\n\n const save = async () => {\n setError(null);\n const input: Record<string, unknown> = {};\n if (form.name.trim() !== initial.name) input.name = form.name.trim();\n if (form.description.trim() !== initial.description.trim())\n input.description = form.description.trim() || null;\n if (amount !== budget.plannedAmount) input.plannedAmount = amount;\n if (form.currency.trim().toUpperCase() !== initial.currency)\n input.currency = form.currency.trim().toUpperCase();\n if (form.periodStart !== initial.periodStart) input.periodStart = form.periodStart || null;\n if (form.periodEnd !== initial.periodEnd) input.periodEnd = form.periodEnd || null;\n if (Object.keys(input).length === 0) {\n setOpen(false);\n return;\n }\n try {\n await throwOnError(update({ variables: { id: budget.id, input } }));\n setOpen(false);\n } catch (e) {\n setError(messageOf(e, 'The budget could not be saved.'));\n }\n };\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (next) setForm(initial);\n else setError(null);\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"budget-edit\">\n <Pencil className=\"mr-1 h-4 w-4\" />\n Edit\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Edit budget</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Change the name, amount, currency, period or description.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n <div className=\"space-y-3\">\n <div>\n <label htmlFor=\"edit-budget-name\" className=\"text-sm font-medium\">\n Name\n </label>\n <Input\n id=\"edit-budget-name\"\n className=\"mt-1\"\n value={form.name}\n onChange={(e) => setForm({ ...form, name: e.target.value })}\n />\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"edit-budget-amount\" className=\"text-sm font-medium\">\n Planned amount\n </label>\n <Input\n id=\"edit-budget-amount\"\n className=\"mt-1\"\n type=\"number\"\n min=\"0\"\n step=\"0.01\"\n inputMode=\"decimal\"\n value={form.plannedAmount}\n onChange={(e) => setForm({ ...form, plannedAmount: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"edit-budget-currency\" className=\"text-sm font-medium\">\n Currency\n </label>\n <Input\n id=\"edit-budget-currency\"\n className=\"mt-1\"\n maxLength={3}\n value={form.currency}\n onChange={(e) => setForm({ ...form, currency: e.target.value.toUpperCase() })}\n />\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label htmlFor=\"edit-budget-start\" className=\"text-sm font-medium\">\n Period start\n </label>\n <Input\n id=\"edit-budget-start\"\n className=\"mt-1\"\n type=\"date\"\n value={form.periodStart}\n onChange={(e) => setForm({ ...form, periodStart: e.target.value })}\n />\n </div>\n <div>\n <label htmlFor=\"edit-budget-end\" className=\"text-sm font-medium\">\n Period end\n </label>\n <Input\n id=\"edit-budget-end\"\n className=\"mt-1\"\n type=\"date\"\n value={form.periodEnd}\n onChange={(e) => setForm({ ...form, periodEnd: e.target.value })}\n />\n </div>\n </div>\n <div>\n <label htmlFor=\"edit-budget-description\" className=\"text-sm font-medium\">\n Description\n </label>\n <textarea\n id=\"edit-budget-description\"\n className={`${FIELD} min-h-[64px]`}\n value={form.description}\n onChange={(e) => setForm({ ...form, description: e.target.value })}\n />\n </div>\n {invertedLocally && (\n <ErrorLine message=\"The period end cannot be before the period start.\" />\n )}\n {error && <ErrorLine message={error} />}\n </div>\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Cancel\n </Button>\n <Button onClick={() => void save()} disabled={!canSubmit} data-testid=\"edit-budget-save\">\n {state.loading ? 'Saving…' : 'Save'}\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /** Log an expense against a budget. */
2
+ export declare function LogExpenseDialog({ budgetId, projectId, refetchQueries, }: {
3
+ budgetId: string;
4
+ /** Narrows the optional "work item" search to the budget's own project, when known. */
5
+ projectId?: string;
6
+ refetchQueries: string[];
7
+ }): import("react/jsx-runtime").JSX.Element;
8
+ export interface ExpenseValues {
9
+ id: string;
10
+ description: string;
11
+ amount: number;
12
+ category: string | null;
13
+ date: string;
14
+ referenceUrl: string | null;
15
+ }
16
+ /** Edit an expense's description, amount, category, date or reference URL. Only changed fields are sent. */
17
+ export declare function EditExpenseDialog({ expense, refetchQueries, }: {
18
+ expense: ExpenseValues;
19
+ refetchQueries: string[];
20
+ }): import("react/jsx-runtime").JSX.Element;
21
+ /** Delete an expense, with confirmation. */
22
+ export declare function DeleteExpenseButton({ id, description, refetchQueries, }: {
23
+ id: string;
24
+ description: string;
25
+ refetchQueries: string[];
26
+ }): import("react/jsx-runtime").JSX.Element;
27
+ //# sourceMappingURL=ExpenseControls.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExpenseControls.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/budgets/ExpenseControls.tsx"],"names":[],"mappings":"AAoEA,uCAAuC;AACvC,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,SAAS,EACT,cAAc,GACf,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,uFAAuF;IACvF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,2CAwKA;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B;AAED,4GAA4G;AAC5G,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,cAAc,GACf,EAAE;IACD,OAAO,EAAE,aAAa,CAAC;IACvB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,2CA+IA;AAED,4CAA4C;AAC5C,wBAAgB,mBAAmB,CAAC,EAClC,EAAE,EACF,WAAW,EACX,cAAc,GACf,EAAE;IACD,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,EAAE,CAAC;CAC1B,2CAiCA"}