@burdenoff/microfe-planmagnet 2026.1001.6 → 2026.1001.8
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/PlanMagnetRoutes.d.ts.map +1 -1
- package/dist/planmagnet/PlanMagnetRoutes.js +10 -6
- package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.d.ts +31 -19
- package/dist/planmagnet/automation/automationRuleTypes.d.ts.map +1 -1
- package/dist/planmagnet/automation/automationRuleTypes.js +4 -8
- package/dist/planmagnet/automation/automationRuleTypes.js.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts +18 -1
- package/dist/planmagnet/controls/FeatureControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/FeatureControls.js +259 -202
- package/dist/planmagnet/controls/FeatureControls.js.map +1 -1
- package/dist/planmagnet/controls/FeedbackControls.d.ts +70 -0
- package/dist/planmagnet/controls/FeedbackControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/FeedbackControls.js +153 -0
- package/dist/planmagnet/controls/FeedbackControls.js.map +1 -0
- package/dist/planmagnet/controls/SyncControls.d.ts.map +1 -1
- package/dist/planmagnet/controls/SyncControls.js +1 -0
- package/dist/planmagnet/controls/SyncControls.js.map +1 -1
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts +47 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.d.ts.map +1 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js +542 -0
- package/dist/planmagnet/controls/WebhookSubscriptionControls.js.map +1 -0
- package/dist/planmagnet/dashboards/DashboardUI.js +7 -7
- package/dist/planmagnet/dashboards/cards/RiskSeverityCard.js +4 -4
- package/dist/planmagnet/forecast/DeliveryForecastCard.js +8 -8
- package/dist/planmagnet/forecast/ProjectOutlookCard.js +10 -10
- package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +9 -9
- package/dist/planmagnet/forecast/ResourceForecastCard.js +6 -6
- package/dist/planmagnet/hooks/useLiveRefresh.d.ts +33 -2
- package/dist/planmagnet/hooks/useLiveRefresh.d.ts.map +1 -1
- package/dist/planmagnet/hooks/useLiveRefresh.js +71 -42
- package/dist/planmagnet/hooks/useLiveRefresh.js.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts +2 -1
- package/dist/planmagnet/integrations/syncTypes.d.ts.map +1 -1
- package/dist/planmagnet/integrations/syncTypes.js +19 -9
- package/dist/planmagnet/integrations/syncTypes.js.map +1 -1
- package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +8 -8
- package/dist/planmagnet/pages/DashboardDashboardPagePage.js +23 -23
- package/dist/planmagnet/pages/DocsDocDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js +340 -248
- package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js +172 -167
- package/dist/planmagnet/pages/IntegrationsSyncDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +92 -83
- package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts +2 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.d.ts.map +1 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js +175 -0
- package/dist/planmagnet/pages/IntegrationsWebhooksPagePage.js.map +1 -0
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js +151 -126
- package/dist/planmagnet/pages/ProductsFeatureDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js +136 -129
- package/dist/planmagnet/pages/ProductsFeaturesPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js +149 -119
- package/dist/planmagnet/pages/ProductsNewFeaturePagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsProductsPagePage.js +13 -13
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js +153 -117
- package/dist/planmagnet/pages/ProductsRoadmapAnalyticsPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.d.ts.map +1 -1
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js +173 -138
- package/dist/planmagnet/pages/ProductsRoadmapDetailPagePage.js.map +1 -1
- package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +9 -9
- package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +8 -8
- package/dist/planmagnet/pages/ProjectsProjectsPagePage.js +12 -12
- package/dist/planmagnet/pages/ReportsReportsPagePage.js +23 -23
- package/dist/planmagnet/pages/TasksMyDashboardPagePage.js +18 -18
- package/dist/planmagnet/pages/TasksMyWorkPagePage.js +10 -10
- package/dist/planmagnet/pages/TasksTimeEntriesPagePage.js +5 -5
- package/dist/planmagnet/pages/analytics/DeliveryHealthCards.js +8 -8
- package/dist/planmagnet/pages/analytics/DeliveryKpisCard.js +6 -6
- package/dist/planmagnet/pages/analytics/MetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/QualityMetricsCard.js +6 -6
- package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +8 -8
- package/dist/planmagnet/pages/analytics/StatusFlowCard.js +7 -7
- package/dist/planmagnet/pages/analytics/ThroughputCard.js +7 -7
- package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +7 -7
- package/dist/planmagnet/people/People.d.ts +14 -0
- package/dist/planmagnet/people/People.d.ts.map +1 -1
- package/dist/planmagnet/people/People.js +36 -22
- package/dist/planmagnet/people/People.js.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.d.ts.map +1 -1
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js +171 -153
- package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js.map +1 -1
- package/dist/planmagnet/webhooks/webhookTypes.d.ts +54 -0
- package/dist/planmagnet/webhooks/webhookTypes.d.ts.map +1 -0
- package/dist/planmagnet/webhooks/webhookTypes.js +17 -0
- package/dist/planmagnet/webhooks/webhookTypes.js.map +1 -0
- package/package.json +1 -1
|
@@ -3,8 +3,8 @@ import { Timer as i, Wallet as a } from "lucide-react";
|
|
|
3
3
|
import { gql as o } from "@apollo/client";
|
|
4
4
|
import { useQuery as s } from "@apollo/client/react";
|
|
5
5
|
import { jsx as c, jsxs as l } from "react/jsx-runtime";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { Card as u, CardContent as d, CardHeader as f, CardTitle as p } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
8
|
//#region src/planmagnet/pages/analytics/DeliveryHealthCards.tsx
|
|
9
9
|
var h = o`
|
|
10
10
|
query GetProjectCycleTime(
|
|
@@ -79,7 +79,7 @@ function v({ label: e, value: t, hint: n }) {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
function y({ workspaceId: e, projectId: t, startDate: n, endDate: r }) {
|
|
82
|
-
let { t: a } =
|
|
82
|
+
let { t: a } = m(), { data: o, loading: g, error: y } = s(h, {
|
|
83
83
|
variables: {
|
|
84
84
|
workspaceId: e,
|
|
85
85
|
projectId: t,
|
|
@@ -88,13 +88,13 @@ function y({ workspaceId: e, projectId: t, startDate: n, endDate: r }) {
|
|
|
88
88
|
},
|
|
89
89
|
skip: !e || !t
|
|
90
90
|
}), b = o?.planmagnetCycleTime, x = [...b?.items ?? []].sort((e, t) => t.cycleTimeHours - e.cycleTimeHours).slice(0, 5);
|
|
91
|
-
return /* @__PURE__ */ l(
|
|
91
|
+
return /* @__PURE__ */ l(u, { children: [/* @__PURE__ */ l(f, { children: [/* @__PURE__ */ l(p, {
|
|
92
92
|
className: "flex items-center gap-2",
|
|
93
93
|
children: [/* @__PURE__ */ c(i, { className: "h-4 w-4" }), a("planmagnet.analytics.cycleTime.title", "Cycle time")]
|
|
94
94
|
}), /* @__PURE__ */ c("p", {
|
|
95
95
|
className: "text-xs text-muted-foreground",
|
|
96
96
|
children: a("planmagnet.analytics.cycleTime.subtitle", "From first moving to In Progress (or creation) to Done, for the selected project.")
|
|
97
|
-
})] }), /* @__PURE__ */ c(
|
|
97
|
+
})] }), /* @__PURE__ */ c(d, { children: t ? g ? /* @__PURE__ */ c("p", {
|
|
98
98
|
className: "text-sm text-muted-foreground",
|
|
99
99
|
children: a("planmagnet.analytics.loading", "Loading…")
|
|
100
100
|
}) : y ? /* @__PURE__ */ l("p", {
|
|
@@ -171,7 +171,7 @@ function y({ workspaceId: e, projectId: t, startDate: n, endDate: r }) {
|
|
|
171
171
|
}) })] });
|
|
172
172
|
}
|
|
173
173
|
function b({ workspaceId: i, projectId: o }) {
|
|
174
|
-
let { t: h } =
|
|
174
|
+
let { t: h } = m(), { data: _, loading: v, error: y } = s(g, {
|
|
175
175
|
variables: {
|
|
176
176
|
workspaceId: i,
|
|
177
177
|
filter: o ? { projectId: o } : void 0
|
|
@@ -183,7 +183,7 @@ function b({ workspaceId: i, projectId: o }) {
|
|
|
183
183
|
"near-limit": h("planmagnet.analytics.budget.nearLimit", "Near limit"),
|
|
184
184
|
"over-budget": h("planmagnet.analytics.budget.over", "Over budget")
|
|
185
185
|
};
|
|
186
|
-
return /* @__PURE__ */ l(
|
|
186
|
+
return /* @__PURE__ */ l(u, { children: [/* @__PURE__ */ l(f, { children: [/* @__PURE__ */ l(p, {
|
|
187
187
|
className: "flex items-center gap-2",
|
|
188
188
|
children: [/* @__PURE__ */ c(a, { className: "h-4 w-4" }), h("planmagnet.analytics.budget.title", "Budget performance")]
|
|
189
189
|
}), /* @__PURE__ */ c("p", {
|
|
@@ -192,7 +192,7 @@ function b({ workspaceId: i, projectId: o }) {
|
|
|
192
192
|
shown: b.length,
|
|
193
193
|
total: x
|
|
194
194
|
}) : o ? h("planmagnet.analytics.budget.subtitleProject", "Spent against plan, per budget, for the selected project.") : h("planmagnet.analytics.budget.subtitle", "Spent against plan, per budget, across the workspace.")
|
|
195
|
-
})] }), /* @__PURE__ */ c(
|
|
195
|
+
})] }), /* @__PURE__ */ c(d, { children: v ? /* @__PURE__ */ c("p", {
|
|
196
196
|
className: "text-sm text-muted-foreground",
|
|
197
197
|
children: h("planmagnet.analytics.loading", "Loading…")
|
|
198
198
|
}) : y ? /* @__PURE__ */ l("p", {
|
|
@@ -3,8 +3,8 @@ import { Info as t, Target as n } from "lucide-react";
|
|
|
3
3
|
import { gql as r } from "@apollo/client";
|
|
4
4
|
import { useQuery as i } from "@apollo/client/react";
|
|
5
5
|
import { jsx as a, jsxs as o } from "react/jsx-runtime";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { Card as s, CardContent as c, CardHeader as l, CardTitle as u } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
8
|
//#region src/planmagnet/pages/analytics/DeliveryKpisCard.tsx
|
|
9
9
|
var f = r`
|
|
10
10
|
query GetAnalyticsDeliveryKpis(
|
|
@@ -71,7 +71,7 @@ function p({ label: e, onTime: t, total: n, withoutPlan: r, rate: i, noPlanNoun:
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
function m({ workspaceId: r, projectId: m, startDate: h, endDate: g }) {
|
|
74
|
-
let { t: _ } =
|
|
74
|
+
let { t: _ } = d(), { data: v, loading: y, error: b } = i(f, {
|
|
75
75
|
variables: {
|
|
76
76
|
workspaceId: r,
|
|
77
77
|
projectId: m,
|
|
@@ -80,15 +80,15 @@ function m({ workspaceId: r, projectId: m, startDate: h, endDate: g }) {
|
|
|
80
80
|
},
|
|
81
81
|
skip: !r
|
|
82
82
|
}), x = v?.planmagnetDeliveryKpis, S = x?.scopeAdherence, C = S?.adherenceRate == null ? null : Math.round(S.adherenceRate * 100);
|
|
83
|
-
return /* @__PURE__ */ o(
|
|
83
|
+
return /* @__PURE__ */ o(s, {
|
|
84
84
|
"data-testid": "delivery-card",
|
|
85
|
-
children: [/* @__PURE__ */ o(
|
|
85
|
+
children: [/* @__PURE__ */ o(l, { children: [/* @__PURE__ */ o(u, {
|
|
86
86
|
className: "flex items-center gap-2",
|
|
87
87
|
children: [/* @__PURE__ */ a(n, { className: "h-4 w-4" }), _("planmagnet.analytics.delivery.kpiTitle", "Delivery")]
|
|
88
88
|
}), /* @__PURE__ */ a("p", {
|
|
89
89
|
className: "text-xs text-muted-foreground",
|
|
90
90
|
children: _("planmagnet.analytics.delivery.kpiSubtitle", "On-time delivery and scope adherence, for sprints/releases/milestones completed in the selected range.")
|
|
91
|
-
})] }), /* @__PURE__ */ a(
|
|
91
|
+
})] }), /* @__PURE__ */ a(c, { children: y ? /* @__PURE__ */ a("p", {
|
|
92
92
|
className: "text-sm text-muted-foreground",
|
|
93
93
|
children: _("planmagnet.analytics.loading", "Loading…")
|
|
94
94
|
}) : b ? /* @__PURE__ */ o("p", {
|
|
@@ -3,8 +3,8 @@ import { Activity as n, Info as r } from "lucide-react";
|
|
|
3
3
|
import { gql as i } from "@apollo/client";
|
|
4
4
|
import { useQuery as a } from "@apollo/client/react";
|
|
5
5
|
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { Card as c, CardContent as l, CardHeader as u, CardTitle as d } from "@burdenoff/fe-libs/ui";
|
|
7
|
+
import { useI18n as f } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
8
8
|
//#region src/planmagnet/pages/analytics/MetricsCard.tsx
|
|
9
9
|
var p = i`
|
|
10
10
|
query GetWorkspaceMetrics($workspaceId: ID!, $timeRange: PlanmagnetTimeRangeInput) {
|
|
@@ -69,7 +69,7 @@ function v({ metric: t }) {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
function y({ workspaceId: e, projectId: t, startDate: i, endDate: h }) {
|
|
72
|
-
let { t: g } =
|
|
72
|
+
let { t: g } = f(), _ = {
|
|
73
73
|
startDate: i,
|
|
74
74
|
endDate: h
|
|
75
75
|
}, { data: y, loading: b, error: x } = a(p, {
|
|
@@ -86,15 +86,15 @@ function y({ workspaceId: e, projectId: t, startDate: i, endDate: h }) {
|
|
|
86
86
|
},
|
|
87
87
|
skip: !e || !t
|
|
88
88
|
}), T = y?.planmagnetMetrics ?? [], E = S?.planmagnetAnalytics.projectMetrics ?? [], D = S?.planmagnetAnalytics.teamMetrics ?? [], O = [...E, ...D];
|
|
89
|
-
return /* @__PURE__ */ s(
|
|
89
|
+
return /* @__PURE__ */ s(c, {
|
|
90
90
|
"data-testid": "metrics-card",
|
|
91
|
-
children: [/* @__PURE__ */ s(
|
|
91
|
+
children: [/* @__PURE__ */ s(u, { children: [/* @__PURE__ */ s(d, {
|
|
92
92
|
className: "flex items-center gap-2",
|
|
93
93
|
children: [/* @__PURE__ */ o(n, { className: "h-4 w-4" }), g("planmagnet.analytics.metrics.title", "Metrics")]
|
|
94
94
|
}), /* @__PURE__ */ o("p", {
|
|
95
95
|
className: "text-xs text-muted-foreground",
|
|
96
96
|
children: g("planmagnet.analytics.metrics.subtitle", "Workspace-wide KPIs, plus the selected project's own metrics, composed from the queries above into one generic shape.")
|
|
97
|
-
})] }), /* @__PURE__ */ s(
|
|
97
|
+
})] }), /* @__PURE__ */ s(l, {
|
|
98
98
|
className: "space-y-4",
|
|
99
99
|
children: [
|
|
100
100
|
/* @__PURE__ */ s("div", { children: [/* @__PURE__ */ o("h3", {
|
|
@@ -12,8 +12,8 @@ import { Bug as d } from "lucide-react";
|
|
|
12
12
|
import { gql as f } from "@apollo/client";
|
|
13
13
|
import { useQuery as p } from "@apollo/client/react";
|
|
14
14
|
import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
15
|
+
import { Card as _, CardContent as v, CardHeader as y, CardTitle as b } from "@burdenoff/fe-libs/ui";
|
|
16
|
+
import { useI18n as x } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
17
17
|
//#region src/planmagnet/pages/analytics/QualityMetricsCard.tsx
|
|
18
18
|
var S = f`
|
|
19
19
|
query GetAnalyticsQualityMetrics(
|
|
@@ -56,7 +56,7 @@ var S = f`
|
|
|
56
56
|
NONE: "var(--color-chart-axis)"
|
|
57
57
|
};
|
|
58
58
|
function T({ workspaceId: f, projectId: T, startDate: E, endDate: D }) {
|
|
59
|
-
let { t: O } =
|
|
59
|
+
let { t: O } = x(), { data: k, loading: A, error: j } = p(S, {
|
|
60
60
|
variables: {
|
|
61
61
|
workspaceId: f,
|
|
62
62
|
projectId: T,
|
|
@@ -69,15 +69,15 @@ function T({ workspaceId: f, projectId: T, startDate: E, endDate: D }) {
|
|
|
69
69
|
count: N.get(e) ?? 0,
|
|
70
70
|
fill: w[e]
|
|
71
71
|
}));
|
|
72
|
-
return /* @__PURE__ */ g(
|
|
72
|
+
return /* @__PURE__ */ g(_, {
|
|
73
73
|
"data-testid": "quality-card",
|
|
74
|
-
children: [/* @__PURE__ */ g(
|
|
74
|
+
children: [/* @__PURE__ */ g(y, { children: [/* @__PURE__ */ g(b, {
|
|
75
75
|
className: "flex items-center gap-2",
|
|
76
76
|
children: [/* @__PURE__ */ h(d, { className: "h-4 w-4" }), O("planmagnet.analytics.quality.title", "Quality")]
|
|
77
77
|
}), /* @__PURE__ */ h("p", {
|
|
78
78
|
className: "text-xs text-muted-foreground",
|
|
79
79
|
children: T ? O("planmagnet.analytics.quality.subtitleProject", "Bug and quality-gate activity for the selected project, in the selected range.") : O("planmagnet.analytics.quality.subtitle", "Bug and quality-gate activity across the workspace, in the selected range.")
|
|
80
|
-
})] }), /* @__PURE__ */ h(
|
|
80
|
+
})] }), /* @__PURE__ */ h(v, { children: A ? /* @__PURE__ */ h("p", {
|
|
81
81
|
className: "text-sm text-muted-foreground",
|
|
82
82
|
children: O("planmagnet.analytics.loading", "Loading…")
|
|
83
83
|
}) : j ? /* @__PURE__ */ g("p", {
|
|
@@ -14,8 +14,8 @@ import { BarChart3 as f, TrendingUp as p } from "lucide-react";
|
|
|
14
14
|
import { gql as m } from "@apollo/client";
|
|
15
15
|
import { useQuery as h } from "@apollo/client/react";
|
|
16
16
|
import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
17
|
+
import { Card as y, CardContent as b, CardHeader as x, CardTitle as S } from "@burdenoff/fe-libs/ui";
|
|
18
|
+
import { useI18n as C } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
19
19
|
//#region src/planmagnet/pages/analytics/SprintDeliveryCards.tsx
|
|
20
20
|
var w = m`
|
|
21
21
|
query DashboardSprintBurndown($workspaceId: ID!, $sprintId: ID!) {
|
|
@@ -60,7 +60,7 @@ function E(e) {
|
|
|
60
60
|
return `${t.getUTCMonth() + 1}/${t.getUTCDate()}`;
|
|
61
61
|
}
|
|
62
62
|
function D({ workspaceId: r, sprintId: i }) {
|
|
63
|
-
let { t: a } =
|
|
63
|
+
let { t: a } = C(), { data: d, loading: p, error: m } = h(w, {
|
|
64
64
|
variables: {
|
|
65
65
|
workspaceId: r,
|
|
66
66
|
sprintId: i
|
|
@@ -71,13 +71,13 @@ function D({ workspaceId: r, sprintId: i }) {
|
|
|
71
71
|
remaining: e.remainingPoints,
|
|
72
72
|
completed: e.completedPoints
|
|
73
73
|
}));
|
|
74
|
-
return /* @__PURE__ */ v(
|
|
74
|
+
return /* @__PURE__ */ v(y, { children: [/* @__PURE__ */ v(x, { children: [/* @__PURE__ */ v(S, {
|
|
75
75
|
className: "flex items-center gap-2",
|
|
76
76
|
children: [/* @__PURE__ */ _(f, { className: "h-4 w-4" }), a("planmagnet.dashboards.burndown.title", "Sprint burndown")]
|
|
77
77
|
}), /* @__PURE__ */ _("p", {
|
|
78
78
|
className: "text-xs text-muted-foreground",
|
|
79
79
|
children: a("planmagnet.dashboards.burndown.subtitle", "Remaining vs. completed story points, by day of the sprint.")
|
|
80
|
-
})] }), /* @__PURE__ */ _(
|
|
80
|
+
})] }), /* @__PURE__ */ _(b, { children: i ? p && !d ? /* @__PURE__ */ _("p", {
|
|
81
81
|
className: "text-sm text-muted-foreground",
|
|
82
82
|
children: a("planmagnet.dashboards.loading", "Loading…")
|
|
83
83
|
}) : m ? /* @__PURE__ */ v("p", {
|
|
@@ -135,7 +135,7 @@ function D({ workspaceId: r, sprintId: i }) {
|
|
|
135
135
|
}) })] });
|
|
136
136
|
}
|
|
137
137
|
function O({ workspaceId: s, projectId: u, trailingSprintCount: f = 6 }) {
|
|
138
|
-
let { t: m } =
|
|
138
|
+
let { t: m } = C(), { data: w, loading: E, error: D } = h(T, {
|
|
139
139
|
variables: {
|
|
140
140
|
workspaceId: s,
|
|
141
141
|
projectId: u,
|
|
@@ -147,13 +147,13 @@ function O({ workspaceId: s, projectId: u, trailingSprintCount: f = 6 }) {
|
|
|
147
147
|
completed: e.completedPoints,
|
|
148
148
|
planned: e.plannedPoints ?? void 0
|
|
149
149
|
})) ?? [], A = O ? Math.round(O.averageVelocity) : 0, j = O ? Math.round(O.forecastNextSprintPoints) : 0, M = O?.forecastRange ?? null, N = O?.insufficientHistory ?? !1;
|
|
150
|
-
return /* @__PURE__ */ v(
|
|
150
|
+
return /* @__PURE__ */ v(y, { children: [/* @__PURE__ */ v(x, { children: [/* @__PURE__ */ v(S, {
|
|
151
151
|
className: "flex items-center gap-2",
|
|
152
152
|
children: [/* @__PURE__ */ _(p, { className: "h-4 w-4" }), m("planmagnet.dashboards.velocity.title", "Sprint velocity")]
|
|
153
153
|
}), /* @__PURE__ */ _("p", {
|
|
154
154
|
className: "text-xs text-muted-foreground",
|
|
155
155
|
children: m("planmagnet.dashboards.velocity.subtitle", "Completed story points per sprint, across the trailing {{count}} completed sprints.", { count: f })
|
|
156
|
-
})] }), /* @__PURE__ */ _(
|
|
156
|
+
})] }), /* @__PURE__ */ _(b, { children: u ? E && !w ? /* @__PURE__ */ _("p", {
|
|
157
157
|
className: "text-sm text-muted-foreground",
|
|
158
158
|
children: m("planmagnet.dashboards.loading", "Loading…")
|
|
159
159
|
}) : D ? /* @__PURE__ */ v("p", {
|
|
@@ -9,8 +9,8 @@ import { Workflow as c } from "lucide-react";
|
|
|
9
9
|
import { gql as l } from "@apollo/client";
|
|
10
10
|
import { useQuery as u } from "@apollo/client/react";
|
|
11
11
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { Card as p, CardContent as m, CardHeader as h, CardTitle as g } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { useI18n as _ } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
14
14
|
//#region src/planmagnet/pages/analytics/StatusFlowCard.tsx
|
|
15
15
|
var v = l`
|
|
16
16
|
query GetAnalyticsStatusFlow(
|
|
@@ -93,7 +93,7 @@ function w({ sourceX: e, sourceY: t, targetX: n, targetY: r, sourceControlX: i,
|
|
|
93
93
|
});
|
|
94
94
|
}
|
|
95
95
|
function T({ workspaceId: i, projectId: o, startDate: l, endDate: b }) {
|
|
96
|
-
let { t: T } =
|
|
96
|
+
let { t: T } = _(), E = s(() => x(l, b), [l, b]), { data: D, loading: O, error: k } = u(v, {
|
|
97
97
|
variables: {
|
|
98
98
|
workspaceId: i,
|
|
99
99
|
projectId: o,
|
|
@@ -102,10 +102,10 @@ function T({ workspaceId: i, projectId: o, startDate: l, endDate: b }) {
|
|
|
102
102
|
},
|
|
103
103
|
skip: !i || !o
|
|
104
104
|
}), A = D?.planmagnetStatusFlow ?? [], { nodes: j, links: M } = S(A), N = [...A].sort((e, t) => t.count - e.count);
|
|
105
|
-
return /* @__PURE__ */ f(
|
|
105
|
+
return /* @__PURE__ */ f(p, {
|
|
106
106
|
"data-testid": "status-flow-card",
|
|
107
|
-
children: [/* @__PURE__ */ f(
|
|
108
|
-
/* @__PURE__ */ f(
|
|
107
|
+
children: [/* @__PURE__ */ f(h, { children: [
|
|
108
|
+
/* @__PURE__ */ f(g, {
|
|
109
109
|
className: "flex items-center gap-2",
|
|
110
110
|
children: [/* @__PURE__ */ d(c, { className: "h-4 w-4" }), T("planmagnet.analytics.statusFlow.title", "Status flow")]
|
|
111
111
|
}),
|
|
@@ -120,7 +120,7 @@ function T({ workspaceId: i, projectId: o, startDate: l, endDate: b }) {
|
|
|
120
120
|
from: E.from.slice(0, 10)
|
|
121
121
|
})
|
|
122
122
|
})
|
|
123
|
-
] }), /* @__PURE__ */ d(
|
|
123
|
+
] }), /* @__PURE__ */ d(m, { children: o ? O && !D ? /* @__PURE__ */ d("div", {
|
|
124
124
|
className: "flex justify-center py-12",
|
|
125
125
|
children: /* @__PURE__ */ d("div", { className: "animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full" })
|
|
126
126
|
}) : k ? /* @__PURE__ */ d("p", {
|
|
@@ -11,8 +11,8 @@ import { BarChart3 as l } from "lucide-react";
|
|
|
11
11
|
import { gql as u } from "@apollo/client";
|
|
12
12
|
import { useQuery as d } from "@apollo/client/react";
|
|
13
13
|
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { Card as h, CardContent as g, CardHeader as _, CardTitle as v } from "@burdenoff/fe-libs/ui";
|
|
15
|
+
import { useI18n as y } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
16
16
|
//#region src/planmagnet/pages/analytics/ThroughputCard.tsx
|
|
17
17
|
var b = u`
|
|
18
18
|
query GetAnalyticsThroughput(
|
|
@@ -43,7 +43,7 @@ function x(e) {
|
|
|
43
43
|
return `${t.getUTCMonth() + 1}/${t.getUTCDate()}`;
|
|
44
44
|
}
|
|
45
45
|
function S({ workspaceId: u, projectId: S, startDate: C, endDate: w }) {
|
|
46
|
-
let { t: T } =
|
|
46
|
+
let { t: T } = y(), [E, D] = c("WEEK"), { data: O, loading: k, error: A } = d(b, {
|
|
47
47
|
variables: {
|
|
48
48
|
workspaceId: u,
|
|
49
49
|
projectId: S,
|
|
@@ -57,11 +57,11 @@ function S({ workspaceId: u, projectId: S, startDate: C, endDate: w }) {
|
|
|
57
57
|
count: e.completedCount,
|
|
58
58
|
points: e.completedPoints
|
|
59
59
|
})), N = j.reduce((e, t) => e + t.completedCount, 0), P = j.reduce((e, t) => e + t.completedPoints, 0);
|
|
60
|
-
return /* @__PURE__ */ m(
|
|
60
|
+
return /* @__PURE__ */ m(h, {
|
|
61
61
|
"data-testid": "analytics-throughput",
|
|
62
|
-
children: [/* @__PURE__ */ m(
|
|
62
|
+
children: [/* @__PURE__ */ m(_, { children: [/* @__PURE__ */ m("div", {
|
|
63
63
|
className: "flex flex-wrap items-center justify-between gap-2",
|
|
64
|
-
children: [/* @__PURE__ */ m(
|
|
64
|
+
children: [/* @__PURE__ */ m(v, {
|
|
65
65
|
className: "flex items-center gap-2",
|
|
66
66
|
children: [/* @__PURE__ */ p(l, { className: "h-4 w-4" }), T("planmagnet.analytics.throughput.title", "Throughput")]
|
|
67
67
|
}), /* @__PURE__ */ p("div", {
|
|
@@ -80,7 +80,7 @@ function S({ workspaceId: u, projectId: S, startDate: C, endDate: w }) {
|
|
|
80
80
|
}), /* @__PURE__ */ p("p", {
|
|
81
81
|
className: "text-xs text-muted-foreground",
|
|
82
82
|
children: T("planmagnet.analytics.throughput.subtitle", "Work items completed per {{bucket}}, in the selected range.", { bucket: E === "DAY" ? T("planmagnet.analytics.throughput.day", "Day").toLowerCase() : T("planmagnet.analytics.throughput.week", "Week").toLowerCase() })
|
|
83
|
-
})] }), /* @__PURE__ */ p(
|
|
83
|
+
})] }), /* @__PURE__ */ p(g, { children: k && !O ? /* @__PURE__ */ p("div", {
|
|
84
84
|
className: "flex justify-center py-12",
|
|
85
85
|
children: /* @__PURE__ */ p("div", { className: "animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full" })
|
|
86
86
|
}) : A ? /* @__PURE__ */ p("p", {
|
|
@@ -10,8 +10,8 @@ import { LayoutGrid as c } from "lucide-react";
|
|
|
10
10
|
import { gql as l } from "@apollo/client";
|
|
11
11
|
import { useQuery as u } from "@apollo/client/react";
|
|
12
12
|
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { Card as p, CardContent as m, CardHeader as h, CardTitle as g } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { useI18n as _ } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
15
15
|
//#region src/planmagnet/pages/analytics/WorkBreakdownCard.tsx
|
|
16
16
|
var v = l`
|
|
17
17
|
query GetAnalyticsWorkBreakdown($workspaceId: ID!, $projectIds: [ID!]) {
|
|
@@ -168,7 +168,7 @@ function T(e) {
|
|
|
168
168
|
});
|
|
169
169
|
}
|
|
170
170
|
function E({ workspaceId: a, projectId: o }) {
|
|
171
|
-
let { t: l } =
|
|
171
|
+
let { t: l } = _(), y = e(), [b, x] = s("items"), { data: S, loading: w, error: E } = u(v, {
|
|
172
172
|
variables: {
|
|
173
173
|
workspaceId: a,
|
|
174
174
|
projectIds: o ? [o] : void 0
|
|
@@ -178,11 +178,11 @@ function E({ workspaceId: a, projectId: o }) {
|
|
|
178
178
|
function M(e) {
|
|
179
179
|
y(`/tasks/task-detail-page?taskId=${encodeURIComponent(e)}`);
|
|
180
180
|
}
|
|
181
|
-
return /* @__PURE__ */ f(
|
|
181
|
+
return /* @__PURE__ */ f(p, {
|
|
182
182
|
"data-testid": "work-breakdown-card",
|
|
183
|
-
children: [/* @__PURE__ */ f(
|
|
183
|
+
children: [/* @__PURE__ */ f(h, { children: [/* @__PURE__ */ f("div", {
|
|
184
184
|
className: "flex flex-wrap items-center justify-between gap-2",
|
|
185
|
-
children: [/* @__PURE__ */ f(
|
|
185
|
+
children: [/* @__PURE__ */ f(g, {
|
|
186
186
|
className: "flex items-center gap-2",
|
|
187
187
|
children: [/* @__PURE__ */ d(c, { className: "h-4 w-4" }), l("planmagnet.analytics.workBreakdown.title", "Work breakdown")]
|
|
188
188
|
}), /* @__PURE__ */ d("div", {
|
|
@@ -201,7 +201,7 @@ function E({ workspaceId: a, projectId: o }) {
|
|
|
201
201
|
}), /* @__PURE__ */ d("p", {
|
|
202
202
|
className: "text-xs text-muted-foreground",
|
|
203
203
|
children: l("planmagnet.analytics.workBreakdown.subtitle", "Live open/done work, by project and epic — current state, not limited by the date range above.")
|
|
204
|
-
})] }), /* @__PURE__ */ d(
|
|
204
|
+
})] }), /* @__PURE__ */ d(m, { children: w && !S ? /* @__PURE__ */ d("div", {
|
|
205
205
|
className: "flex justify-center py-12",
|
|
206
206
|
children: /* @__PURE__ */ d("div", { className: "animate-spin h-6 w-6 border-2 border-primary border-t-transparent rounded-full" })
|
|
207
207
|
}) : E ? /* @__PURE__ */ d("p", {
|
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import { ActorProfile } from '@burdenoff/fe-libs/shared';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* CWF-15's redaction sentinel: `FeatureRequest.submittedBy`/`createdBy`/`updatedBy` and `FeatureVote.voterId`
|
|
6
|
+
* read back this literal string, instead of the real actor id, for a non-moderator caller when the record (or,
|
|
7
|
+
* for a vote, that vote) is flagged `isAnonymous`. Never a real actor id — those are workspace-member uuids —
|
|
8
|
+
* so, like the `automation:` actor ids above, it must never be sent through `useActorProfiles`: that would only
|
|
9
|
+
* ever resolve to the generic "not a member" fallback, and would burn a wasted slot in every batch lookup.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ANONYMOUS_ACTOR_ID = "anonymous";
|
|
12
|
+
export declare function isAnonymousActorId(id: string | null | undefined): boolean;
|
|
13
|
+
/** The literal "Anonymous" badge CWF-15 asks for everywhere a redacted actor id would otherwise show — see
|
|
14
|
+
* `PersonName` below, which is the one place `submittedBy`/`createdBy`/`updatedBy`/`voterId` ever render. */
|
|
15
|
+
export declare function AnonymousBadge({ className }: {
|
|
16
|
+
className?: string;
|
|
17
|
+
}): import("react/jsx-runtime").JSX.Element;
|
|
4
18
|
export declare function PeopleProvider({ workspaceId, children, }: {
|
|
5
19
|
workspaceId?: string;
|
|
6
20
|
children: ReactNode;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"People.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/people/People.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,2BAA2B,CAAC;
|
|
1
|
+
{"version":3,"file":"People.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/people/People.tsx"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,YAAY,EAElB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AA4Cf;;;;;;GAMG;AACH,eAAO,MAAM,kBAAkB,cAAc,CAAC;AAE9C,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAEzE;AAED;8GAC8G;AAC9G,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,2CAMnE;AAID,wBAAgB,cAAc,CAAC,EAC7B,WAAW,EACX,QAAQ,GACT,EAAE;IACD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,SAAS,CAAC;CACrB,2CAkBA;AAED,MAAM,WAAW,eAAe;IAC9B,sDAAsD;IACtD,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;GAMG;AACH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,YAAY,GAAG,IAAI,CA2BnF;AAED,wBAAgB,UAAU,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,eAAe,2CAW5D;AAED,mFAAmF;AACnF,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ/C;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CAahE"}
|
|
@@ -1,48 +1,62 @@
|
|
|
1
1
|
import { createContext as e, useCallback as t, useContext as n, useEffect as r, useMemo as i, useState as a } from "react";
|
|
2
2
|
import { jsx as o } from "react/jsx-runtime";
|
|
3
3
|
import { buildActorFallback as s, useActorProfiles as c } from "@burdenoff/fe-libs/shared";
|
|
4
|
+
import { Badge as l } from "@burdenoff/fe-libs/ui";
|
|
4
5
|
//#region src/planmagnet/people/People.tsx
|
|
5
|
-
var
|
|
6
|
+
var u = {
|
|
6
7
|
"automation:engine": "Automation",
|
|
7
8
|
"automation:scheduler": "Automation"
|
|
8
|
-
},
|
|
9
|
-
function
|
|
9
|
+
}, d = "anonymous";
|
|
10
|
+
function f(e) {
|
|
11
|
+
return e === d;
|
|
12
|
+
}
|
|
13
|
+
function p({ className: e }) {
|
|
14
|
+
return /* @__PURE__ */ o(l, {
|
|
15
|
+
variant: "secondary",
|
|
16
|
+
className: e ?? "text-xs",
|
|
17
|
+
"data-testid": "anonymous-badge",
|
|
18
|
+
children: "Anonymous"
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
var m = e(null);
|
|
22
|
+
function h({ workspaceId: e, children: n }) {
|
|
10
23
|
let [r, s] = a([]), l = t((e) => {
|
|
11
24
|
s((t) => t.includes(e) ? t : [...t, e]);
|
|
12
|
-
}, []),
|
|
25
|
+
}, []), u = c(i(() => r.map((e) => ({
|
|
13
26
|
actorId: e,
|
|
14
27
|
actorType: "user"
|
|
15
|
-
})), [r]), { workspaceId: e }),
|
|
28
|
+
})), [r]), { workspaceId: e }), d = i(() => ({
|
|
16
29
|
request: l,
|
|
17
|
-
profiles:
|
|
18
|
-
}), [l,
|
|
19
|
-
return /* @__PURE__ */ o(
|
|
20
|
-
value:
|
|
30
|
+
profiles: u
|
|
31
|
+
}), [l, u]);
|
|
32
|
+
return /* @__PURE__ */ o(m.Provider, {
|
|
33
|
+
value: d,
|
|
21
34
|
children: n
|
|
22
35
|
});
|
|
23
36
|
}
|
|
24
|
-
function
|
|
25
|
-
let t = n(
|
|
37
|
+
function g(e) {
|
|
38
|
+
let t = n(m), i = t?.request, a = e ? u[e] : void 0, o = f(e);
|
|
26
39
|
return r(() => {
|
|
27
|
-
e && i && !a && i(e);
|
|
40
|
+
e && i && !a && !o && i(e);
|
|
28
41
|
}, [
|
|
29
42
|
e,
|
|
30
43
|
i,
|
|
31
|
-
a
|
|
44
|
+
a,
|
|
45
|
+
o
|
|
32
46
|
]), e ? a ? {
|
|
33
47
|
actorId: e,
|
|
34
48
|
actorType: "service",
|
|
35
49
|
displayName: a,
|
|
36
50
|
secondaryLabel: "Automation",
|
|
37
51
|
resolved: !0
|
|
38
|
-
} : t?.profiles[e] ?? s({
|
|
52
|
+
} : o ? null : t?.profiles[e] ?? s({
|
|
39
53
|
actorId: e,
|
|
40
54
|
actorType: "user"
|
|
41
55
|
}) : null;
|
|
42
56
|
}
|
|
43
|
-
function
|
|
44
|
-
let n =
|
|
45
|
-
return n ? /* @__PURE__ */ o("span", {
|
|
57
|
+
function _({ id: e, className: t }) {
|
|
58
|
+
let n = g(e);
|
|
59
|
+
return f(e) ? /* @__PURE__ */ o(p, { className: t }) : n ? /* @__PURE__ */ o("span", {
|
|
46
60
|
className: t,
|
|
47
61
|
title: n.email ?? void 0,
|
|
48
62
|
children: n.displayName
|
|
@@ -51,19 +65,19 @@ function p({ id: e, className: t }) {
|
|
|
51
65
|
children: "—"
|
|
52
66
|
});
|
|
53
67
|
}
|
|
54
|
-
function
|
|
68
|
+
function v(e) {
|
|
55
69
|
return e.trim().split(/\s+/).filter(Boolean).slice(0, 2).map((e) => e[0].toUpperCase()).join("");
|
|
56
70
|
}
|
|
57
|
-
function
|
|
58
|
-
let n =
|
|
71
|
+
function y({ id: e, className: t }) {
|
|
72
|
+
let n = g(e), r = n ? n.email ? `${n.displayName} (${n.email})` : n.displayName : void 0;
|
|
59
73
|
return /* @__PURE__ */ o("span", {
|
|
60
74
|
className: t,
|
|
61
75
|
title: r,
|
|
62
76
|
"aria-label": r,
|
|
63
|
-
children: (n?.resolved ?
|
|
77
|
+
children: (n?.resolved ? v(n.displayName) : "") || "?"
|
|
64
78
|
});
|
|
65
79
|
}
|
|
66
80
|
//#endregion
|
|
67
|
-
export {
|
|
81
|
+
export { h as PeopleProvider, y as PersonAvatar, _ as PersonName, v as initialsOf, g as usePersonProfile };
|
|
68
82
|
|
|
69
83
|
//# sourceMappingURL=People.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"People.js","names":[],"sources":["../../../src/planmagnet/people/People.tsx"],"sourcesContent":["import {\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n type ActorProfile,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\n/**\n * Show people by name, not by UUID.\n *\n * Until 2026-09-24, 21 places in this MFE rendered a raw user id where a\n * person belonged — project owners, roadmap owners, assignees, the team lead.\n * fe-libs already resolves ids to workspace-member identities\n * (`useActorProfiles`, with a 5-minute cache), but it batches per CALL and\n * does not dedupe across calls, so calling it from every row would fire a\n * burst of lookups on the first render of each list.\n *\n * So there is exactly one call, here. Every `<PersonName id>` on screen\n * registers its id with the provider; ids registered during the same render\n * land in one state update, and the provider resolves them together. A\n * growing list costs one request for the NEW ids only — already-resolved ids\n * come from the fe-libs cache.\n *\n * An id that cannot be resolved (not a member of this workspace, lookup\n * failed) renders fe-libs' short fallback, \"User 1a2b3c\", rather than the full\n * UUID: still identifiable, never a wall of hex.\n */\n\ninterface PeopleContextValue {\n request: (id: string) => void;\n profiles: ActorProfileMap;\n}\n\n/**\n * The automation engine's own actor identities (BOFF-8047 follow-up) — never a real workspace member's id, so\n * they are mapped explicitly rather than sent through `useActorProfiles` (which would only ever resolve them to\n * its generic \"not a member\" fallback). Sourced directly from wspace-planmagnet-svc, not guessed:\n * - `automation-engine.ts`'s `AUTOMATION_COMMENT_AUTHOR_ID` (`'automation:engine'`) — the fixed author of every\n * comment a POST_COMMENT action posts, regardless of which trigger or actor caused the run.\n * - `automation-scheduler.ts`'s `AUTOMATION_SCHEDULER_ACTOR_ID` (`'automation:scheduler'`) — the actor behind a\n * SCHEDULE-triggered run (threaded through as `updatedBy` on that run's other actions).\n * No real actor id can ever collide with the `automation:` prefix (workspace-member ids are uuids) — see that\n * constant's own doc comment in the engine.\n */\nconst AUTOMATION_ACTOR_NAMES: Record<string, string> = {\n 'automation:engine': 'Automation',\n 'automation:scheduler': 'Automation',\n};\n\nconst PeopleContext = createContext<PeopleContextValue | null>(null);\n\nexport function PeopleProvider({\n workspaceId,\n children,\n}: {\n workspaceId?: string;\n children: ReactNode;\n}) {\n const [ids, setIds] = useState<readonly string[]>([]);\n\n const request = useCallback((id: string) => {\n setIds((previous) => (previous.includes(id) ? previous : [...previous, id]));\n }, []);\n\n const actors = useMemo<ActorInput[]>(\n () => ids.map((actorId) => ({ actorId, actorType: 'user' })),\n [ids]\n );\n\n // No apiGatewayUrl on purpose: this MFE is handed the WORKSPACE gateway, and\n // the hook resolves the right gateways from the hostname itself.\n const profiles = useActorProfiles(actors, { workspaceId });\n\n const value = useMemo(() => ({ request, profiles }), [request, profiles]);\n return <PeopleContext.Provider value={value}>{children}</PeopleContext.Provider>;\n}\n\nexport interface PersonNameProps {\n /** A user id. Empty or missing renders an em dash. */\n id: string | null | undefined;\n className?: string;\n}\n\n/**\n * Inline text on purpose. fe-libs' `ActorIdentity` is a block (avatar plus two\n * stacked paragraphs); every place this replaces is an inline label —\n * \"Owner: …\", a table cell, a <span> inside a <p> — where a block would break\n * the layout and nest a <div> inside a <p>. The email, when known, is the\n * hover title, so two people with the same name stay distinguishable.\n */\n/**\n * Register an id with the provider and return its profile: the resolved identity, or fe-libs' short fallback\n * (\"User 1a2b3c\", `resolved: false`) until and unless it resolves. `null` for an empty id.\n */\nexport function usePersonProfile(id: string | null | undefined): ActorProfile | null {\n const context = useContext(PeopleContext);\n const request = context?.request;\n const automationName = id ? AUTOMATION_ACTOR_NAMES[id] : undefined;\n\n useEffect(() => {\n // An automation identity is never a real workspace member — registering it with the provider would only\n // ever come back as the generic \"not a member\" fallback, and would cost a wasted slot in every batch lookup.\n if (id && request && !automationName) request(id);\n }, [id, request, automationName]);\n\n if (!id) return null;\n if (automationName) {\n return {\n actorId: id,\n actorType: 'service',\n displayName: automationName,\n secondaryLabel: 'Automation',\n resolved: true,\n };\n }\n return context?.profiles[id] ?? buildActorFallback({ actorId: id, actorType: 'user' });\n}\n\nexport function PersonName({ id, className }: PersonNameProps) {\n const profile = usePersonProfile(id);\n if (!profile) return <span className={className ?? 'text-muted-foreground'}>—</span>;\n return (\n <span className={className} title={profile.email ?? undefined}>\n {profile.displayName}\n </span>\n );\n}\n\n/** Up to two initials from a display name (\"Ada Lovelace\" → \"AL\", \"ada\" → \"A\"). */\nexport function initialsOf(name: string): string {\n return name\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((word) => word[0]!.toUpperCase())\n .join('');\n}\n\nexport interface PersonAvatarProps {\n id: string | null | undefined;\n className?: string;\n}\n\n/**\n * An initials bubble for a user id. Initials come from the RESOLVED name; until the id resolves (or when it never\n * does) the bubble shows a neutral \"?\" rather than letters cut from the UUID, which matched nobody (BOFF-7977,\n * BOFF-8000). The tooltip is the name, plus the email when known.\n */\nexport function PersonAvatar({ id, className }: PersonAvatarProps) {\n const profile = usePersonProfile(id);\n const label = profile\n ? profile.email\n ? `${profile.displayName} (${profile.email})`\n : profile.displayName\n : undefined;\n const initials = profile?.resolved ? initialsOf(profile.displayName) : '';\n return (\n <span className={className} title={label} aria-label={label}>\n {initials || '?'}\n </span>\n );\n}\n"],"mappings":";;;;AAsDA,IAAM,IAAiD;CACrD,qBAAqB;CACrB,wBAAwB;CACzB,EAEK,IAAgB,EAAyC,KAAK;AAEpE,SAAgB,EAAe,EAC7B,gBACA,eAIC;CACD,IAAM,CAAC,GAAK,KAAU,EAA4B,EAAE,CAAC,EAE/C,IAAU,GAAa,MAAe;AAC1C,KAAQ,MAAc,EAAS,SAAS,EAAG,GAAG,IAAW,CAAC,GAAG,GAAU,EAAG,CAAE;IAC3E,EAAE,CAAC,EASA,IAAW,EAPF,QACP,EAAI,KAAK,OAAa;EAAE;EAAS,WAAW;EAAQ,EAAE,EAC5D,CAAC,EAAI,CACN,EAIyC,EAAE,gBAAa,CAAC,EAEpD,IAAQ,SAAe;EAAE;EAAS;EAAU,GAAG,CAAC,GAAS,EAAS,CAAC;AACzE,QAAO,kBAAC,EAAc,UAAf;EAA+B;EAAQ;EAAkC,CAAA;;AAoBlF,SAAgB,EAAiB,GAAoD;CACnF,IAAM,IAAU,EAAW,EAAc,EACnC,IAAU,GAAS,SACnB,IAAiB,IAAK,EAAuB,KAAM,KAAA;AAkBzD,QAhBA,QAAgB;AAGd,EAAI,KAAM,KAAW,CAAC,KAAgB,EAAQ,EAAG;IAChD;EAAC;EAAI;EAAS;EAAe,CAAC,EAE5B,IACD,IACK;EACL,SAAS;EACT,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,UAAU;EACX,GAEI,GAAS,SAAS,MAAO,EAAmB;EAAE,SAAS;EAAI,WAAW;EAAQ,CAAC,GAVtE;;AAalB,SAAgB,EAAW,EAAE,OAAI,gBAA8B;CAC7D,IAAM,IAAU,EAAiB,EAAG;AAEpC,QADK,IAEH,kBAAC,QAAD;EAAiB;EAAW,OAAO,EAAQ,SAAS,KAAA;YACjD,EAAQ;EACJ,CAAA,GAJY,kBAAC,QAAD;EAAM,WAAW,KAAa;YAAyB;EAAQ,CAAA;;AAStF,SAAgB,EAAW,GAAsB;AAC/C,QAAO,EACJ,MAAM,CACN,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,MAAM,GAAG,EAAE,CACX,KAAK,MAAS,EAAK,GAAI,aAAa,CAAC,CACrC,KAAK,GAAG;;AAab,SAAgB,EAAa,EAAE,OAAI,gBAAgC;CACjE,IAAM,IAAU,EAAiB,EAAG,EAC9B,IAAQ,IACV,EAAQ,QACN,GAAG,EAAQ,YAAY,IAAI,EAAQ,MAAM,KACzC,EAAQ,cACV,KAAA;AAEJ,QACE,kBAAC,QAAD;EAAiB;EAAW,OAAO;EAAO,cAAY;aAFvC,GAAS,WAAW,EAAW,EAAQ,YAAY,GAAG,OAGtD;EACR,CAAA"}
|
|
1
|
+
{"version":3,"file":"People.js","names":[],"sources":["../../../src/planmagnet/people/People.tsx"],"sourcesContent":["import {\n buildActorFallback,\n useActorProfiles,\n type ActorInput,\n type ActorProfile,\n type ActorProfileMap,\n} from '@burdenoff/fe-libs/shared';\nimport { Badge } from '@burdenoff/fe-libs/ui';\nimport {\n createContext,\n useCallback,\n useContext,\n useEffect,\n useMemo,\n useState,\n type ReactNode,\n} from 'react';\n\n/**\n * Show people by name, not by UUID.\n *\n * Until 2026-09-24, 21 places in this MFE rendered a raw user id where a\n * person belonged — project owners, roadmap owners, assignees, the team lead.\n * fe-libs already resolves ids to workspace-member identities\n * (`useActorProfiles`, with a 5-minute cache), but it batches per CALL and\n * does not dedupe across calls, so calling it from every row would fire a\n * burst of lookups on the first render of each list.\n *\n * So there is exactly one call, here. Every `<PersonName id>` on screen\n * registers its id with the provider; ids registered during the same render\n * land in one state update, and the provider resolves them together. A\n * growing list costs one request for the NEW ids only — already-resolved ids\n * come from the fe-libs cache.\n *\n * An id that cannot be resolved (not a member of this workspace, lookup\n * failed) renders fe-libs' short fallback, \"User 1a2b3c\", rather than the full\n * UUID: still identifiable, never a wall of hex.\n */\n\ninterface PeopleContextValue {\n request: (id: string) => void;\n profiles: ActorProfileMap;\n}\n\n/**\n * The automation engine's own actor identities (BOFF-8047 follow-up) — never a real workspace member's id, so\n * they are mapped explicitly rather than sent through `useActorProfiles` (which would only ever resolve them to\n * its generic \"not a member\" fallback). Sourced directly from wspace-planmagnet-svc, not guessed:\n * - `automation-engine.ts`'s `AUTOMATION_COMMENT_AUTHOR_ID` (`'automation:engine'`) — the fixed author of every\n * comment a POST_COMMENT action posts, regardless of which trigger or actor caused the run.\n * - `automation-scheduler.ts`'s `AUTOMATION_SCHEDULER_ACTOR_ID` (`'automation:scheduler'`) — the actor behind a\n * SCHEDULE-triggered run (threaded through as `updatedBy` on that run's other actions).\n * No real actor id can ever collide with the `automation:` prefix (workspace-member ids are uuids) — see that\n * constant's own doc comment in the engine.\n */\nconst AUTOMATION_ACTOR_NAMES: Record<string, string> = {\n 'automation:engine': 'Automation',\n 'automation:scheduler': 'Automation',\n};\n\n/**\n * CWF-15's redaction sentinel: `FeatureRequest.submittedBy`/`createdBy`/`updatedBy` and `FeatureVote.voterId`\n * read back this literal string, instead of the real actor id, for a non-moderator caller when the record (or,\n * for a vote, that vote) is flagged `isAnonymous`. Never a real actor id — those are workspace-member uuids —\n * so, like the `automation:` actor ids above, it must never be sent through `useActorProfiles`: that would only\n * ever resolve to the generic \"not a member\" fallback, and would burn a wasted slot in every batch lookup.\n */\nexport const ANONYMOUS_ACTOR_ID = 'anonymous';\n\nexport function isAnonymousActorId(id: string | null | undefined): boolean {\n return id === ANONYMOUS_ACTOR_ID;\n}\n\n/** The literal \"Anonymous\" badge CWF-15 asks for everywhere a redacted actor id would otherwise show — see\n * `PersonName` below, which is the one place `submittedBy`/`createdBy`/`updatedBy`/`voterId` ever render. */\nexport function AnonymousBadge({ className }: { className?: string }) {\n return (\n <Badge variant=\"secondary\" className={className ?? 'text-xs'} data-testid=\"anonymous-badge\">\n Anonymous\n </Badge>\n );\n}\n\nconst PeopleContext = createContext<PeopleContextValue | null>(null);\n\nexport function PeopleProvider({\n workspaceId,\n children,\n}: {\n workspaceId?: string;\n children: ReactNode;\n}) {\n const [ids, setIds] = useState<readonly string[]>([]);\n\n const request = useCallback((id: string) => {\n setIds((previous) => (previous.includes(id) ? previous : [...previous, id]));\n }, []);\n\n const actors = useMemo<ActorInput[]>(\n () => ids.map((actorId) => ({ actorId, actorType: 'user' })),\n [ids]\n );\n\n // No apiGatewayUrl on purpose: this MFE is handed the WORKSPACE gateway, and\n // the hook resolves the right gateways from the hostname itself.\n const profiles = useActorProfiles(actors, { workspaceId });\n\n const value = useMemo(() => ({ request, profiles }), [request, profiles]);\n return <PeopleContext.Provider value={value}>{children}</PeopleContext.Provider>;\n}\n\nexport interface PersonNameProps {\n /** A user id. Empty or missing renders an em dash. */\n id: string | null | undefined;\n className?: string;\n}\n\n/**\n * Inline text on purpose. fe-libs' `ActorIdentity` is a block (avatar plus two\n * stacked paragraphs); every place this replaces is an inline label —\n * \"Owner: …\", a table cell, a <span> inside a <p> — where a block would break\n * the layout and nest a <div> inside a <p>. The email, when known, is the\n * hover title, so two people with the same name stay distinguishable.\n */\n/**\n * Register an id with the provider and return its profile: the resolved identity, or fe-libs' short fallback\n * (\"User 1a2b3c\", `resolved: false`) until and unless it resolves. `null` for an empty id.\n */\nexport function usePersonProfile(id: string | null | undefined): ActorProfile | null {\n const context = useContext(PeopleContext);\n const request = context?.request;\n const automationName = id ? AUTOMATION_ACTOR_NAMES[id] : undefined;\n const anonymous = isAnonymousActorId(id);\n\n useEffect(() => {\n // An automation identity is never a real workspace member, and the CWF-15 \"anonymous\" sentinel is never an\n // actor id at all — registering either with the provider would only ever come back as the generic \"not a\n // member\" fallback, and would cost a wasted slot in every batch lookup.\n if (id && request && !automationName && !anonymous) request(id);\n }, [id, request, automationName, anonymous]);\n\n if (!id) return null;\n if (automationName) {\n return {\n actorId: id,\n actorType: 'service',\n displayName: automationName,\n secondaryLabel: 'Automation',\n resolved: true,\n };\n }\n // CWF-15: \"anonymous\" is a redaction sentinel, never a real actor id — PersonName/PersonAvatar below render it\n // specially rather than resolving it (which would only ever land on the generic \"not a member\" fallback).\n if (anonymous) return null;\n return context?.profiles[id] ?? buildActorFallback({ actorId: id, actorType: 'user' });\n}\n\nexport function PersonName({ id, className }: PersonNameProps) {\n const profile = usePersonProfile(id);\n // CWF-15: the literal \"anonymous\" sentinel renders as the Anonymous badge everywhere a person's name would\n // otherwise show — never resolved as, or displayed like, a real user id.\n if (isAnonymousActorId(id)) return <AnonymousBadge className={className} />;\n if (!profile) return <span className={className ?? 'text-muted-foreground'}>—</span>;\n return (\n <span className={className} title={profile.email ?? undefined}>\n {profile.displayName}\n </span>\n );\n}\n\n/** Up to two initials from a display name (\"Ada Lovelace\" → \"AL\", \"ada\" → \"A\"). */\nexport function initialsOf(name: string): string {\n return name\n .trim()\n .split(/\\s+/)\n .filter(Boolean)\n .slice(0, 2)\n .map((word) => word[0]!.toUpperCase())\n .join('');\n}\n\nexport interface PersonAvatarProps {\n id: string | null | undefined;\n className?: string;\n}\n\n/**\n * An initials bubble for a user id. Initials come from the RESOLVED name; until the id resolves (or when it never\n * does) the bubble shows a neutral \"?\" rather than letters cut from the UUID, which matched nobody (BOFF-7977,\n * BOFF-8000). The tooltip is the name, plus the email when known.\n */\nexport function PersonAvatar({ id, className }: PersonAvatarProps) {\n const profile = usePersonProfile(id);\n const label = profile\n ? profile.email\n ? `${profile.displayName} (${profile.email})`\n : profile.displayName\n : undefined;\n const initials = profile?.resolved ? initialsOf(profile.displayName) : '';\n return (\n <span className={className} title={label} aria-label={label}>\n {initials || '?'}\n </span>\n );\n}\n"],"mappings":";;;;;AAuDA,IAAM,IAAiD;CACrD,qBAAqB;CACrB,wBAAwB;CACzB,EASY,IAAqB;AAElC,SAAgB,EAAmB,GAAwC;AACzE,QAAO,MAAO;;AAKhB,SAAgB,EAAe,EAAE,gBAAqC;AACpE,QACE,kBAAC,GAAD;EAAO,SAAQ;EAAY,WAAW,KAAa;EAAW,eAAY;YAAkB;EAEpF,CAAA;;AAIZ,IAAM,IAAgB,EAAyC,KAAK;AAEpE,SAAgB,EAAe,EAC7B,gBACA,eAIC;CACD,IAAM,CAAC,GAAK,KAAU,EAA4B,EAAE,CAAC,EAE/C,IAAU,GAAa,MAAe;AAC1C,KAAQ,MAAc,EAAS,SAAS,EAAG,GAAG,IAAW,CAAC,GAAG,GAAU,EAAG,CAAE;IAC3E,EAAE,CAAC,EASA,IAAW,EAPF,QACP,EAAI,KAAK,OAAa;EAAE;EAAS,WAAW;EAAQ,EAAE,EAC5D,CAAC,EAAI,CACN,EAIyC,EAAE,gBAAa,CAAC,EAEpD,IAAQ,SAAe;EAAE;EAAS;EAAU,GAAG,CAAC,GAAS,EAAS,CAAC;AACzE,QAAO,kBAAC,EAAc,UAAf;EAA+B;EAAQ;EAAkC,CAAA;;AAoBlF,SAAgB,EAAiB,GAAoD;CACnF,IAAM,IAAU,EAAW,EAAc,EACnC,IAAU,GAAS,SACnB,IAAiB,IAAK,EAAuB,KAAM,KAAA,GACnD,IAAY,EAAmB,EAAG;AAsBxC,QApBA,QAAgB;AAId,EAAI,KAAM,KAAW,CAAC,KAAkB,CAAC,KAAW,EAAQ,EAAG;IAC9D;EAAC;EAAI;EAAS;EAAgB;EAAU,CAAC,EAEvC,IACD,IACK;EACL,SAAS;EACT,WAAW;EACX,aAAa;EACb,gBAAgB;EAChB,UAAU;EACX,GAIC,IAAkB,OACf,GAAS,SAAS,MAAO,EAAmB;EAAE,SAAS;EAAI,WAAW;EAAQ,CAAC,GAbtE;;AAgBlB,SAAgB,EAAW,EAAE,OAAI,gBAA8B;CAC7D,IAAM,IAAU,EAAiB,EAAG;AAKpC,QAFI,EAAmB,EAAG,GAAS,kBAAC,GAAD,EAA2B,cAAa,CAAA,GACtE,IAEH,kBAAC,QAAD;EAAiB;EAAW,OAAO,EAAQ,SAAS,KAAA;YACjD,EAAQ;EACJ,CAAA,GAJY,kBAAC,QAAD;EAAM,WAAW,KAAa;YAAyB;EAAQ,CAAA;;AAStF,SAAgB,EAAW,GAAsB;AAC/C,QAAO,EACJ,MAAM,CACN,MAAM,MAAM,CACZ,OAAO,QAAQ,CACf,MAAM,GAAG,EAAE,CACX,KAAK,MAAS,EAAK,GAAI,aAAa,CAAC,CACrC,KAAK,GAAG;;AAab,SAAgB,EAAa,EAAE,OAAI,gBAAgC;CACjE,IAAM,IAAU,EAAiB,EAAG,EAC9B,IAAQ,IACV,EAAQ,QACN,GAAG,EAAQ,YAAY,IAAI,EAAQ,MAAM,KACzC,EAAQ,cACV,KAAA;AAEJ,QACE,kBAAC,QAAD;EAAiB;EAAW,OAAO;EAAO,cAAY;aAFvC,GAAS,WAAW,EAAW,EAAQ,YAAY,GAAG,OAGtD;EACR,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RoadmapNowNextLater.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/roadmaps/RoadmapNowNextLater.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"RoadmapNowNextLater.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/roadmaps/RoadmapNowNextLater.tsx"],"names":[],"mappings":"AA4FA,UAAU,OAAO;IACf,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,OAAO,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IACzC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5C,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,oBAAoB,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CAClE;AA4ID;;qHAEqH;AACrH,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,IAAI,GACL,EAAE;IACD,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB,kDA4BA;AAED,4FAA4F;AAC5F,wBAAgB,eAAe,CAAC,EAAE,UAAU,EAAE,EAAE;IAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,kDAW5E;AA0JD,wBAAgB,mBAAmB,CAAC,EAAE,SAAS,EAAE,EAAE;IAAE,SAAS,EAAE,MAAM,CAAA;CAAE,2CAmYvE"}
|