@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.
- package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoot.js +117 -93
- package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +156 -76
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/automation/AutomationExecutionsPanel.d.ts +5 -0
- package/dist/planmagnet/automation/AutomationExecutionsPanel.d.ts.map +1 -0
- package/dist/planmagnet/automation/AutomationExecutionsPanel.js +107 -0
- package/dist/planmagnet/automation/AutomationExecutionsPanel.js.map +1 -0
- package/dist/planmagnet/automation/AutomationRuleFields.d.ts +29 -0
- package/dist/planmagnet/automation/AutomationRuleFields.d.ts.map +1 -0
- package/dist/planmagnet/automation/AutomationRuleFields.js +308 -0
- package/dist/planmagnet/automation/AutomationRuleFields.js.map +1 -0
- package/dist/planmagnet/automation/automationRuleTypes.d.ts +60 -0
- package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -0
- package/dist/planmagnet/automation/automationRuleTypes.js +113 -0
- package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -0
- package/dist/planmagnet/board/BulkActions.d.ts +11 -0
- package/dist/planmagnet/board/BulkActions.d.ts.map +1 -0
- package/dist/planmagnet/board/BulkActions.js +182 -0
- package/dist/planmagnet/board/BulkActions.js.map +1 -0
- package/dist/planmagnet/board/ProjectBoard.d.ts.map +1 -1
- package/dist/planmagnet/board/ProjectBoard.js +289 -254
- package/dist/planmagnet/board/ProjectBoard.js.map +1 -1
- package/dist/planmagnet/budgets/BudgetControls.d.ts +30 -0
- package/dist/planmagnet/budgets/BudgetControls.d.ts.map +1 -0
- package/dist/planmagnet/budgets/BudgetControls.js +470 -0
- package/dist/planmagnet/budgets/BudgetControls.js.map +1 -0
- package/dist/planmagnet/budgets/ExpenseControls.d.ts +27 -0
- package/dist/planmagnet/budgets/ExpenseControls.d.ts.map +1 -0
- package/dist/planmagnet/budgets/ExpenseControls.js +366 -0
- package/dist/planmagnet/budgets/ExpenseControls.js.map +1 -0
- package/dist/planmagnet/budgets/ProjectBudgetsSummary.d.ts +5 -0
- package/dist/planmagnet/budgets/ProjectBudgetsSummary.d.ts.map +1 -0
- package/dist/planmagnet/budgets/ProjectBudgetsSummary.js +112 -0
- package/dist/planmagnet/budgets/ProjectBudgetsSummary.js.map +1 -0
- package/dist/planmagnet/budgets/budgetMath.d.ts +26 -0
- package/dist/planmagnet/budgets/budgetMath.d.ts.map +1 -0
- package/dist/planmagnet/budgets/budgetMath.js +49 -0
- package/dist/planmagnet/budgets/budgetMath.js.map +1 -0
- package/dist/planmagnet/controls/AutomationRuleControls.d.ts +23 -0
- package/dist/planmagnet/controls/AutomationRuleControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/AutomationRuleControls.js +172 -0
- package/dist/planmagnet/controls/AutomationRuleControls.js.map +1 -0
- package/dist/planmagnet/controls/CompetitorControls.js +15 -15
- package/dist/planmagnet/controls/CustomFieldControls.d.ts +42 -0
- package/dist/planmagnet/controls/CustomFieldControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/CustomFieldControls.js +84 -0
- package/dist/planmagnet/controls/CustomFieldControls.js.map +1 -0
- package/dist/planmagnet/controls/CustomFieldValuesPanel.d.ts +8 -0
- package/dist/planmagnet/controls/CustomFieldValuesPanel.d.ts.map +1 -0
- package/dist/planmagnet/controls/CustomFieldValuesPanel.js +315 -0
- package/dist/planmagnet/controls/CustomFieldValuesPanel.js.map +1 -0
- package/dist/planmagnet/controls/FeatureControls.js +54 -54
- package/dist/planmagnet/controls/ObjectiveControls.js +15 -15
- package/dist/planmagnet/controls/PortfolioControls.js +7 -7
- package/dist/planmagnet/controls/ProgramControls.js +19 -19
- package/dist/planmagnet/controls/ProjectControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/ProjectControls.js +54 -47
- package/dist/planmagnet/controls/ProjectControls.js.map +1 -1
- package/dist/planmagnet/controls/ReleaseControls.js +7 -7
- package/dist/planmagnet/controls/RiskControls.d.ts +27 -0
- package/dist/planmagnet/controls/RiskControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/RiskControls.js +346 -0
- package/dist/planmagnet/controls/RiskControls.js.map +1 -0
- package/dist/planmagnet/controls/TemplateControls.d.ts +15 -0
- package/dist/planmagnet/controls/TemplateControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/TemplateControls.js +55 -0
- package/dist/planmagnet/controls/TemplateControls.js.map +1 -0
- package/dist/planmagnet/controls/WorkItemControls.js +1 -1
- package/dist/planmagnet/create/CreateDialogs.d.ts +20 -0
- package/dist/planmagnet/create/CreateDialogs.d.ts.map +1 -1
- package/dist/planmagnet/create/CreateDialogs.js +455 -116
- package/dist/planmagnet/create/CreateDialogs.js.map +1 -1
- package/dist/planmagnet/dependency/DependencyPanel.d.ts +6 -0
- package/dist/planmagnet/dependency/DependencyPanel.d.ts.map +1 -0
- package/dist/planmagnet/dependency/DependencyPanel.js +310 -0
- package/dist/planmagnet/dependency/DependencyPanel.js.map +1 -0
- package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js +171 -0
- package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js +232 -0
- package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts +2 -0
- package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js +93 -0
- package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js.map +1 -0
- package/dist/planmagnet/pages/PortfoliosEditPortfolioPagePage.js +5 -5
- package/dist/planmagnet/pages/PortfoliosPortfolioDetailPagePage.js +9 -9
- package/dist/planmagnet/pages/PortfoliosPortfoliosPagePage.js +6 -6
- package/dist/planmagnet/pages/ProgramsCreateProgramPagePage.js +4 -4
- package/dist/planmagnet/pages/ProgramsEditProgramPagePage.js +6 -6
- package/dist/planmagnet/pages/ProgramsProgramsPagePage.js +5 -5
- package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js +315 -0
- package/dist/planmagnet/pages/ProjectsBudgetDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +186 -0
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js +164 -0
- package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js +348 -0
- package/dist/planmagnet/pages/ProjectsCustomFieldsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +10 -10
- package/dist/planmagnet/pages/ProjectsMilestoneDetailPagePage.js +4 -4
- package/dist/planmagnet/pages/ProjectsPlanningDetailPagePage.js +4 -4
- package/dist/planmagnet/pages/ProjectsPlanningEditPagePage.js +6 -6
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +319 -203
- package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +81 -72
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/RisksRiskDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/RisksRiskDetailPagePage.js +278 -0
- package/dist/planmagnet/pages/RisksRiskDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/RisksRiskRegisterPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js +191 -0
- package/dist/planmagnet/pages/RisksRiskRegisterPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksCreateTaskPagePage.js +5 -5
- package/dist/planmagnet/pages/TasksEditTaskPagePage.js +4 -4
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js +161 -137
- package/dist/planmagnet/pages/TasksTaskDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/TasksTasksPagePage.js +124 -113
- package/dist/planmagnet/pages/TasksTasksPagePage.js.map +1 -1
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +263 -0
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js +254 -0
- package/dist/planmagnet/pages/TasksTimesheetDetailPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TasksTimesheetsPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TasksTimesheetsPagePage.js +165 -0
- package/dist/planmagnet/pages/TasksTimesheetsPagePage.js.map +1 -0
- package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js +316 -0
- package/dist/planmagnet/pages/TemplatesEditTemplatePagePage.js.map +1 -0
- package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/TemplatesTemplatesPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js +208 -0
- package/dist/planmagnet/pages/TemplatesTemplatesPagePage.js.map +1 -0
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts +0 -5
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.d.ts.map +1 -1
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +47 -89
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapControls.js +24 -24
- package/dist/planmagnet/time/TaskTimePanel.d.ts +7 -0
- package/dist/planmagnet/time/TaskTimePanel.d.ts.map +1 -0
- package/dist/planmagnet/time/TaskTimePanel.js +104 -0
- package/dist/planmagnet/time/TaskTimePanel.js.map +1 -0
- package/dist/planmagnet/time/TimeEntryControls.d.ts +37 -0
- package/dist/planmagnet/time/TimeEntryControls.d.ts.map +1 -0
- package/dist/planmagnet/time/TimeEntryControls.js +338 -0
- package/dist/planmagnet/time/TimeEntryControls.js.map +1 -0
- package/dist/planmagnet/time/TimesheetControls.d.ts +18 -0
- package/dist/planmagnet/time/TimesheetControls.d.ts.map +1 -0
- package/dist/planmagnet/time/TimesheetControls.js +244 -0
- package/dist/planmagnet/time/TimesheetControls.js.map +1 -0
- package/dist/planmagnet/time/timeShared.d.ts +29 -0
- package/dist/planmagnet/time/timeShared.d.ts.map +1 -0
- package/dist/planmagnet/time/timeShared.js +34 -0
- package/dist/planmagnet/time/timeShared.js.map +1 -0
- package/dist/planmagnet/utils/rowNavigation.js +5 -2
- package/dist/planmagnet/utils/rowNavigation.js.map +1 -1
- 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 {
|
|
4
|
-
import {
|
|
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:
|
|
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" :
|
|
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]) => `${
|
|
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(
|
|
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:
|
|
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(
|
|
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:
|
|
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 {
|
|
4
|
-
import {
|
|
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" :
|
|
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
|
-
...
|
|
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:
|
|
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" :
|
|
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 {
|
|
4
|
-
import {
|
|
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 =
|
|
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:
|
|
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 {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
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 =
|
|
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(
|
|
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:
|
|
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 {
|
|
4
|
-
import {
|
|
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" :
|
|
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
|
-
...
|
|
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:
|
|
116
|
+
children: r(e.status)
|
|
117
117
|
})]
|
|
118
118
|
})
|
|
119
119
|
}), /* @__PURE__ */ m(v, {
|
|
@@ -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 @@
|
|
|
1
|
+
{"version":3,"file":"ProjectsBudgetsPagePage.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/pages/ProjectsBudgetsPagePage.tsx"],"names":[],"mappings":"AA8DA,wBAAgB,uBAAuB,4CAmKtC"}
|