@burdenoff/microfe-planmagnet 2026.930.21 → 2026.930.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (127) hide show
  1. package/dist/node_modules/lodash/_arrayEach.js +14 -0
  2. package/dist/node_modules/lodash/_arrayEach.js.map +1 -0
  3. package/dist/node_modules/lodash/_assignValue.js +17 -0
  4. package/dist/node_modules/lodash/_assignValue.js.map +1 -0
  5. package/dist/node_modules/lodash/_baseAssign.js +16 -0
  6. package/dist/node_modules/lodash/_baseAssign.js.map +1 -0
  7. package/dist/node_modules/lodash/_baseAssignIn.js +16 -0
  8. package/dist/node_modules/lodash/_baseAssignIn.js.map +1 -0
  9. package/dist/node_modules/lodash/_baseClone.js +64 -0
  10. package/dist/node_modules/lodash/_baseClone.js.map +1 -0
  11. package/dist/node_modules/lodash/_baseCreate.js +21 -0
  12. package/dist/node_modules/lodash/_baseCreate.js.map +1 -0
  13. package/dist/node_modules/lodash/_baseIsMap.js +16 -0
  14. package/dist/node_modules/lodash/_baseIsMap.js.map +1 -0
  15. package/dist/node_modules/lodash/_baseIsSet.js +16 -0
  16. package/dist/node_modules/lodash/_baseIsSet.js.map +1 -0
  17. package/dist/node_modules/lodash/_baseKeysIn.js +20 -0
  18. package/dist/node_modules/lodash/_baseKeysIn.js.map +1 -0
  19. package/dist/node_modules/lodash/_baseSum.js +17 -0
  20. package/dist/node_modules/lodash/_baseSum.js.map +1 -0
  21. package/dist/node_modules/lodash/_baseUnset.js +26 -0
  22. package/dist/node_modules/lodash/_baseUnset.js.map +1 -0
  23. package/dist/node_modules/lodash/_cloneArrayBuffer.js +16 -0
  24. package/dist/node_modules/lodash/_cloneArrayBuffer.js.map +1 -0
  25. package/dist/node_modules/lodash/_cloneBuffer.js +17 -0
  26. package/dist/node_modules/lodash/_cloneBuffer.js.map +1 -0
  27. package/dist/node_modules/lodash/_cloneDataView.js +16 -0
  28. package/dist/node_modules/lodash/_cloneDataView.js.map +1 -0
  29. package/dist/node_modules/lodash/_cloneRegExp.js +15 -0
  30. package/dist/node_modules/lodash/_cloneRegExp.js.map +1 -0
  31. package/dist/node_modules/lodash/_cloneSymbol.js +15 -0
  32. package/dist/node_modules/lodash/_cloneSymbol.js.map +1 -0
  33. package/dist/node_modules/lodash/_cloneTypedArray.js +16 -0
  34. package/dist/node_modules/lodash/_cloneTypedArray.js.map +1 -0
  35. package/dist/node_modules/lodash/_copyArray.js +15 -0
  36. package/dist/node_modules/lodash/_copyArray.js.map +1 -0
  37. package/dist/node_modules/lodash/_copyObject.js +22 -0
  38. package/dist/node_modules/lodash/_copyObject.js.map +1 -0
  39. package/dist/node_modules/lodash/_copySymbols.js +16 -0
  40. package/dist/node_modules/lodash/_copySymbols.js.map +1 -0
  41. package/dist/node_modules/lodash/_copySymbolsIn.js +16 -0
  42. package/dist/node_modules/lodash/_copySymbolsIn.js.map +1 -0
  43. package/dist/node_modules/lodash/_customOmitClone.js +15 -0
  44. package/dist/node_modules/lodash/_customOmitClone.js.map +1 -0
  45. package/dist/node_modules/lodash/_flatRest.js +17 -0
  46. package/dist/node_modules/lodash/_flatRest.js.map +1 -0
  47. package/dist/node_modules/lodash/_getAllKeysIn.js +17 -0
  48. package/dist/node_modules/lodash/_getAllKeysIn.js.map +1 -0
  49. package/dist/node_modules/lodash/_getSymbolsIn.js +18 -0
  50. package/dist/node_modules/lodash/_getSymbolsIn.js.map +1 -0
  51. package/dist/node_modules/lodash/_initCloneArray.js +15 -0
  52. package/dist/node_modules/lodash/_initCloneArray.js.map +1 -0
  53. package/dist/node_modules/lodash/_initCloneByTag.js +40 -0
  54. package/dist/node_modules/lodash/_initCloneByTag.js.map +1 -0
  55. package/dist/node_modules/lodash/_initCloneObject.js +17 -0
  56. package/dist/node_modules/lodash/_initCloneObject.js.map +1 -0
  57. package/dist/node_modules/lodash/_nativeKeysIn.js +15 -0
  58. package/dist/node_modules/lodash/_nativeKeysIn.js.map +1 -0
  59. package/dist/node_modules/lodash/_parent.js +16 -0
  60. package/dist/node_modules/lodash/_parent.js.map +1 -0
  61. package/dist/node_modules/lodash/flatten.js +15 -0
  62. package/dist/node_modules/lodash/flatten.js.map +1 -0
  63. package/dist/node_modules/lodash/isMap.js +14 -0
  64. package/dist/node_modules/lodash/isMap.js.map +1 -0
  65. package/dist/node_modules/lodash/isSet.js +14 -0
  66. package/dist/node_modules/lodash/isSet.js.map +1 -0
  67. package/dist/node_modules/lodash/keysIn.js +17 -0
  68. package/dist/node_modules/lodash/keysIn.js.map +1 -0
  69. package/dist/node_modules/lodash/maxBy.js +17 -0
  70. package/dist/node_modules/lodash/maxBy.js.map +1 -0
  71. package/dist/node_modules/lodash/omit.js +28 -0
  72. package/dist/node_modules/lodash/omit.js.map +1 -0
  73. package/dist/node_modules/lodash/sumBy.js +16 -0
  74. package/dist/node_modules/lodash/sumBy.js.map +1 -0
  75. package/dist/node_modules/recharts/es6/chart/Sankey.js +556 -0
  76. package/dist/node_modules/recharts/es6/chart/Sankey.js.map +1 -0
  77. package/dist/node_modules/recharts/es6/chart/Treemap.js +545 -0
  78. package/dist/node_modules/recharts/es6/chart/Treemap.js.map +1 -0
  79. package/dist/node_modules/recharts/es6/shape/Polygon.js +112 -0
  80. package/dist/node_modules/recharts/es6/shape/Polygon.js.map +1 -0
  81. package/dist/node_modules/recharts/es6/util/Constants.js +31 -0
  82. package/dist/node_modules/recharts/es6/util/Constants.js.map +1 -0
  83. package/dist/node_modules/recharts/es6/util/ReactUtils.js +15 -10
  84. package/dist/node_modules/recharts/es6/util/ReactUtils.js.map +1 -1
  85. package/dist/planmagnet/controls/AutomationRuleControls.js +10 -10
  86. package/dist/planmagnet/controls/CompetitorControls.js +7 -7
  87. package/dist/planmagnet/controls/FeatureControls.js +38 -38
  88. package/dist/planmagnet/controls/ProgramControls.js +8 -8
  89. package/dist/planmagnet/dependency/DependencyGraph.d.ts.map +1 -1
  90. package/dist/planmagnet/dependency/DependencyGraph.js +13 -10
  91. package/dist/planmagnet/dependency/DependencyGraph.js.map +1 -1
  92. package/dist/planmagnet/dependency/DependencyPanel.js +13 -13
  93. package/dist/planmagnet/dependency/GanttChart.d.ts.map +1 -1
  94. package/dist/planmagnet/dependency/GanttChart.js +11 -5
  95. package/dist/planmagnet/dependency/GanttChart.js.map +1 -1
  96. package/dist/planmagnet/pages/ActivityActivityPagePage.js +16 -16
  97. package/dist/planmagnet/pages/ActivityActivityPagePage.js.map +1 -1
  98. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  99. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +91 -73
  100. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  101. package/dist/planmagnet/pages/AutomationAutomationRulesPagePage.js +6 -6
  102. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js +4 -4
  103. package/dist/planmagnet/pages/DashboardDashboardPagePage.js +11 -11
  104. package/dist/planmagnet/pages/DashboardsProjectManagerPagePage.js +5 -5
  105. package/dist/planmagnet/pages/DashboardsTeamUtilizationPagePage.js +5 -5
  106. package/dist/planmagnet/pages/IntegrationsNewSyncPagePage.js +6 -6
  107. package/dist/planmagnet/pages/IntegrationsNewSyncPagePage.js.map +1 -1
  108. package/dist/planmagnet/pages/IntegrationsSyncsPagePage.js +8 -8
  109. package/dist/planmagnet/pages/ProjectsBudgetsPagePage.js +10 -10
  110. package/dist/planmagnet/pages/ProjectsCriticalPathPagePage.js +6 -6
  111. package/dist/planmagnet/pages/ProjectsEditProjectPagePage.js +5 -5
  112. package/dist/planmagnet/pages/ProjectsPlanningPagePage.js +8 -8
  113. package/dist/planmagnet/pages/SprintsStartSprintPagePage.js +4 -4
  114. package/dist/planmagnet/pages/analytics/StatusFlowCard.d.ts +8 -0
  115. package/dist/planmagnet/pages/analytics/StatusFlowCard.d.ts.map +1 -0
  116. package/dist/planmagnet/pages/analytics/StatusFlowCard.js +234 -0
  117. package/dist/planmagnet/pages/analytics/StatusFlowCard.js.map +1 -0
  118. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.d.ts +6 -0
  119. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.d.ts.map +1 -0
  120. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js +367 -0
  121. package/dist/planmagnet/pages/analytics/WorkBreakdownCard.js.map +1 -0
  122. package/dist/planmagnet/roadmaps/RoadmapControls.js +17 -17
  123. package/dist/planmagnet/utils/statusColor.d.ts +8 -9
  124. package/dist/planmagnet/utils/statusColor.d.ts.map +1 -1
  125. package/dist/planmagnet/utils/statusColor.js +8 -40
  126. package/dist/planmagnet/utils/statusColor.js.map +1 -1
  127. package/package.json +1 -1
@@ -2,8 +2,8 @@ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
3
3
  import { ConfirmButton as r, ErrorLine as i } from "./ConfirmButton.js";
4
4
  import { enumLabel as a } from "./WorkItemControls.js";
5
- import { ProjectSelect as o, WorkItemSearchSelect as s } from "./Pickers.js";
6
- import { detailPath as c } from "../utils/rowNavigation.js";
5
+ import { detailPath as o } from "../utils/rowNavigation.js";
6
+ import { ProjectSelect as s, WorkItemSearchSelect as c } from "./Pickers.js";
7
7
  import { parseIntField as l } from "../utils/formValidation.js";
8
8
  import { useState as u } from "react";
9
9
  import { BarChart3 as d, Pencil as f, ThumbsUp as p, Trash2 as m } from "lucide-react";
@@ -81,15 +81,15 @@ function F({ featureId: e, voted: t }) {
81
81
  }), c && /* @__PURE__ */ _(i, { message: c })]
82
82
  });
83
83
  }
84
- function I({ feature: o }) {
85
- let s = e(), [l, f] = g(M, P), [p, h] = g(N, {
84
+ function I({ feature: s }) {
85
+ let c = e(), [l, f] = g(M, P), [p, h] = g(N, {
86
86
  refetchQueries: k.filter((e) => e !== "GetFeatureRequest"),
87
87
  context: t
88
- }), [b, x] = u(null), S = f.loading || h.loading, C = O.includes(o.status) ? O : [o.status, ...O], w = async (e) => {
88
+ }), [b, x] = u(null), S = f.loading || h.loading, C = O.includes(s.status) ? O : [s.status, ...O], w = async (e) => {
89
89
  x(null);
90
90
  try {
91
91
  await n(l({ variables: {
92
- id: o.id,
92
+ id: s.id,
93
93
  input: { status: e }
94
94
  } }));
95
95
  } catch (e) {
@@ -98,7 +98,7 @@ function I({ feature: o }) {
98
98
  }, T = async () => {
99
99
  x(null);
100
100
  try {
101
- await n(p({ variables: { id: o.id } })), s("/products/features-page");
101
+ await n(p({ variables: { id: s.id } })), c("/products/features-page");
102
102
  } catch (e) {
103
103
  x(e instanceof Error ? e.message : "The request could not be deleted.");
104
104
  }
@@ -112,29 +112,29 @@ function I({ feature: o }) {
112
112
  "aria-label": "Status",
113
113
  "data-testid": "feature-status",
114
114
  className: "rounded-md border border-input bg-background px-2 py-1 text-sm disabled:opacity-60",
115
- value: o.status,
115
+ value: s.status,
116
116
  disabled: S,
117
117
  onChange: (e) => {
118
- e.target.value !== o.status && w(e.target.value);
118
+ e.target.value !== s.status && w(e.target.value);
119
119
  },
120
120
  children: C.map((e) => /* @__PURE__ */ _("option", {
121
121
  value: e,
122
122
  children: a(e)
123
123
  }, e))
124
124
  }),
125
- /* @__PURE__ */ _(L, { feature: o }),
125
+ /* @__PURE__ */ _(L, { feature: s }),
126
126
  /* @__PURE__ */ v(y, {
127
127
  size: "sm",
128
128
  variant: "outline",
129
129
  "data-testid": "feature-analytics",
130
- onClick: () => s(c("/products/feature-analytics-page", o.id)),
130
+ onClick: () => c(o("/products/feature-analytics-page", s.id)),
131
131
  children: [/* @__PURE__ */ _(d, { className: "mr-1 h-4 w-4" }), "Analytics"]
132
132
  }),
133
133
  /* @__PURE__ */ _(r, {
134
134
  label: "Delete",
135
135
  variant: "outline",
136
136
  icon: /* @__PURE__ */ _(m, { className: "mr-1 h-4 w-4" }),
137
- title: `Delete "${o.title}"?`,
137
+ title: `Delete "${s.title}"?`,
138
138
  description: "The request and its votes are deleted. This cannot be undone here.",
139
139
  confirmLabel: "Delete request",
140
140
  destructive: !0,
@@ -153,34 +153,34 @@ function L({ feature: e }) {
153
153
  effortEstimate: e.effortEstimate ?? "",
154
154
  projectId: e.projectId ?? "",
155
155
  workItemId: e.workItemId ?? ""
156
- }, [r, a] = u(!1), [c, d] = u(t), [p, m] = u(null), [h, O] = g(M, P), k = l(c.businessValue, {
156
+ }, [r, a] = u(!1), [o, d] = u(t), [p, m] = u(null), [h, O] = g(M, P), k = l(o.businessValue, {
157
157
  min: 0,
158
158
  max: 100
159
- }), A = c.title.trim() !== "" && !k.invalid && !O.loading, j = async () => {
159
+ }), A = o.title.trim() !== "" && !k.invalid && !O.loading, j = async () => {
160
160
  m(null);
161
161
  let r = (e) => e.trim() ? e.trim() : null, i = {
162
- title: c.title.trim(),
163
- description: r(c.description),
162
+ title: o.title.trim(),
163
+ description: r(o.description),
164
164
  businessValue: k.value ?? null,
165
- effortEstimate: r(c.effortEstimate),
166
- projectId: c.projectId || null,
167
- workItemId: c.workItemId || null
168
- }, o = {
165
+ effortEstimate: r(o.effortEstimate),
166
+ projectId: o.projectId || null,
167
+ workItemId: o.workItemId || null
168
+ }, s = {
169
169
  title: t.title,
170
170
  description: r(t.description),
171
171
  businessValue: t.businessValue === "" ? null : Number(t.businessValue),
172
172
  effortEstimate: r(t.effortEstimate),
173
173
  projectId: t.projectId || null,
174
174
  workItemId: t.workItemId || null
175
- }, s = Object.fromEntries(Object.entries(i).filter(([e, t]) => t !== o[e]));
176
- if (Object.keys(s).length === 0) {
175
+ }, c = Object.fromEntries(Object.entries(i).filter(([e, t]) => t !== s[e]));
176
+ if (Object.keys(c).length === 0) {
177
177
  a(!1);
178
178
  return;
179
179
  }
180
180
  try {
181
181
  await n(h({ variables: {
182
182
  id: e.id,
183
- input: s
183
+ input: c
184
184
  } })), a(!1);
185
185
  } catch (e) {
186
186
  m(e instanceof Error ? e.message : "The request could not be saved.");
@@ -213,9 +213,9 @@ function L({ feature: e }) {
213
213
  }), /* @__PURE__ */ _(b, {
214
214
  id: "feature-edit-title",
215
215
  className: "mt-1",
216
- value: c.title,
216
+ value: o.title,
217
217
  onChange: (e) => d({
218
- ...c,
218
+ ...o,
219
219
  title: e.target.value
220
220
  })
221
221
  })] }),
@@ -226,9 +226,9 @@ function L({ feature: e }) {
226
226
  }), /* @__PURE__ */ _("textarea", {
227
227
  id: "feature-edit-description",
228
228
  className: "mt-1 w-full rounded-md border border-input bg-background px-3 py-2 text-sm min-h-[72px]",
229
- value: c.description,
229
+ value: o.description,
230
230
  onChange: (e) => d({
231
- ...c,
231
+ ...o,
232
232
  description: e.target.value
233
233
  })
234
234
  })] }),
@@ -244,10 +244,10 @@ function L({ feature: e }) {
244
244
  min: 0,
245
245
  max: 100,
246
246
  className: "mt-1",
247
- value: c.businessValue,
247
+ value: o.businessValue,
248
248
  "aria-invalid": k.invalid,
249
249
  onChange: (e) => d({
250
- ...c,
250
+ ...o,
251
251
  businessValue: e.target.value
252
252
  })
253
253
  })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
@@ -258,9 +258,9 @@ function L({ feature: e }) {
258
258
  id: "feature-edit-effort",
259
259
  className: "mt-1",
260
260
  placeholder: "e.g. M",
261
- value: c.effortEstimate,
261
+ value: o.effortEstimate,
262
262
  onChange: (e) => d({
263
- ...c,
263
+ ...o,
264
264
  effortEstimate: e.target.value
265
265
  })
266
266
  })] })]
@@ -272,12 +272,12 @@ function L({ feature: e }) {
272
272
  children: "Project"
273
273
  }), /* @__PURE__ */ _("div", {
274
274
  className: "mt-1",
275
- children: /* @__PURE__ */ _(o, {
275
+ children: /* @__PURE__ */ _(s, {
276
276
  id: "feature-edit-project",
277
- value: c.projectId,
277
+ value: o.projectId,
278
278
  noneLabel: "No project",
279
279
  onChange: (e) => d({
280
- ...c,
280
+ ...o,
281
281
  projectId: e,
282
282
  workItemId: ""
283
283
  })
@@ -289,12 +289,12 @@ function L({ feature: e }) {
289
289
  children: "Delivered by work item"
290
290
  }), /* @__PURE__ */ _("div", {
291
291
  className: "mt-1",
292
- children: /* @__PURE__ */ _(s, {
292
+ children: /* @__PURE__ */ _(c, {
293
293
  id: "feature-edit-workitem",
294
- projectId: c.projectId || void 0,
295
- value: c.workItemId,
294
+ projectId: o.projectId || void 0,
295
+ value: o.workItemId,
296
296
  onChange: (e) => d({
297
- ...c,
297
+ ...o,
298
298
  workItemId: e
299
299
  })
300
300
  })
@@ -2,8 +2,8 @@ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { LEAVES_PAGE_CONTEXT as t, throwOnError as n } from "../utils/mutationResult.js";
3
3
  import { ConfirmButton as r, ErrorLine as i } from "./ConfirmButton.js";
4
4
  import { enumLabel as a } from "./WorkItemControls.js";
5
- import { ProjectSelect as o } from "./Pickers.js";
6
- import { detailPath as s } from "../utils/rowNavigation.js";
5
+ import { detailPath as o } from "../utils/rowNavigation.js";
6
+ import { ProjectSelect as s } from "./Pickers.js";
7
7
  import { PROJECT_STATUSES as c } from "./ProjectControls.js";
8
8
  import { useState as l } from "react";
9
9
  import { Pencil as u, Trash2 as d, X as f } from "lucide-react";
@@ -28,7 +28,7 @@ var v = c, y = [
28
28
  planmagnetDeleteProgram(id: $id)
29
29
  }
30
30
  `;
31
- function S({ programId: o, name: c, status: f }) {
31
+ function S({ programId: s, name: c, status: f }) {
32
32
  let p = e(), [S, C] = m(b, {
33
33
  refetchQueries: y,
34
34
  awaitRefetchQueries: !0
@@ -39,7 +39,7 @@ function S({ programId: o, name: c, status: f }) {
39
39
  D(null);
40
40
  try {
41
41
  await n(S({ variables: {
42
- id: o,
42
+ id: s,
43
43
  input: { status: e }
44
44
  } }));
45
45
  } catch (e) {
@@ -48,7 +48,7 @@ function S({ programId: o, name: c, status: f }) {
48
48
  }, j = async () => {
49
49
  D(null);
50
50
  try {
51
- await n(w({ variables: { id: o } })), p("/products/programs-page");
51
+ await n(w({ variables: { id: s } })), p("/products/programs-page");
52
52
  } catch (e) {
53
53
  D(e instanceof Error ? e.message : "The program could not be deleted.");
54
54
  }
@@ -76,7 +76,7 @@ function S({ programId: o, name: c, status: f }) {
76
76
  size: "sm",
77
77
  variant: "outline",
78
78
  "data-testid": "program-edit",
79
- onClick: () => p(s("/products/edit-program-page", o)),
79
+ onClick: () => p(o("/products/edit-program-page", s)),
80
80
  children: [/* @__PURE__ */ h(u, { className: "mr-1 h-4 w-4" }), "Edit"]
81
81
  }),
82
82
  /* @__PURE__ */ h(r, {
@@ -142,7 +142,7 @@ function w({ programId: t, projects: r }) {
142
142
  /* @__PURE__ */ h("button", {
143
143
  type: "button",
144
144
  className: "flex-1 text-left text-sm font-medium hover:underline underline-offset-2",
145
- onClick: () => c(s("/projects/project-detail-page", e.id)),
145
+ onClick: () => c(o("/projects/project-detail-page", e.id)),
146
146
  children: e.name
147
147
  }),
148
148
  /* @__PURE__ */ h("span", {
@@ -163,7 +163,7 @@ function w({ programId: t, projects: r }) {
163
163
  className: "flex items-center gap-2 pt-2 border-t",
164
164
  children: [/* @__PURE__ */ h("div", {
165
165
  className: "flex-1",
166
- children: /* @__PURE__ */ h(o, {
166
+ children: /* @__PURE__ */ h(s, {
167
167
  id: "program-add-project",
168
168
  value: u,
169
169
  onChange: d,
@@ -1 +1 @@
1
- {"version":3,"file":"DependencyGraph.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/dependency/DependencyGraph.tsx"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,6FAA6F;IAC7F,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,2FAA2F;IAC3F,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,SAAS,EAAE,MAAM,CAAC;CACnB;AAWD,uGAAuG;AACvG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAMjE;AA0CD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,KAAK,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,YAAY,EACZ,SAAS,GACV,EAAE,oBAAoB,2CA0MtB"}
1
+ {"version":3,"file":"DependencyGraph.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/dependency/DependencyGraph.tsx"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,gFAAgF;IAChF,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,KAAK,EAAE,mBAAmB,EAAE,CAAC;IAC7B,6FAA6F;IAC7F,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,2FAA2F;IAC3F,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAC;IACrB,wFAAwF;IACxF,SAAS,EAAE,MAAM,CAAC;CACnB;AAWD,uGAAuG;AACvG,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/D;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAaxD;AAED,wBAAgB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,GAAG,IAAI,CAMjE;AA0CD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,KAAK,EACL,eAAe,EACf,eAAe,EACf,cAAc,EACd,YAAY,EACZ,SAAS,GACV,EAAE,oBAAoB,2CA6MtB"}
@@ -1,6 +1,6 @@
1
1
  import { enumLabel as e } from "../controls/WorkItemControls.js";
2
- import { rowLinkProps as t } from "../utils/rowNavigation.js";
3
- import { STATUS_LEGEND_ORDER as n, statusVisual as r } from "../utils/statusColor.js";
2
+ import { STATUS_LEGEND_ORDER as t, statusVisual as n } from "../utils/statusColor.js";
3
+ import { rowLinkProps as r } from "../utils/rowNavigation.js";
4
4
  import { useMemo as i } from "react";
5
5
  import { jsx as a, jsxs as o } from "react/jsx-runtime";
6
6
  //#region src/planmagnet/dependency/DependencyGraph.tsx
@@ -66,7 +66,7 @@ function y({ nodes: y, edges: b, criticalNodeIds: x, criticalEdgeIds: S, onActiv
66
66
  width: d * 2 + a * s + Math.max(0, a - 1) * l,
67
67
  height: d * 2 + o * c + Math.max(0, o - 1) * u
68
68
  };
69
- }, [y, b]), D = i(() => n.filter((e) => y.some((t) => t.status === e)), [y]);
69
+ }, [y, b]), D = i(() => t.filter((e) => y.some((t) => t.status === e)), [y]);
70
70
  if (y.length === 0) return /* @__PURE__ */ a("div", {
71
71
  "data-testid": "dependency-graph",
72
72
  className: "p-6 text-center text-sm text-muted-foreground",
@@ -164,14 +164,14 @@ function y({ nodes: y, edges: b, criticalNodeIds: x, criticalEdgeIds: S, onActiv
164
164
  ]
165
165
  }, e.id);
166
166
  }),
167
- y.map((n) => {
168
- let i = E.position.get(n.id);
167
+ y.map((t) => {
168
+ let i = E.position.get(t.id);
169
169
  if (!i) return null;
170
- let l = x?.has(n.id) ?? !1, u = r(n.status), d = n.title ?? "Untitled work item", g = n.key ?? "", _ = `${g ? `${g} — ` : ""}${d} (${e(n.status)})${l ? ", on critical path" : ""}`;
170
+ let l = x?.has(t.id) ?? !1, u = n(t.status), d = t.title ?? "Untitled work item", g = t.key ?? "", _ = `${g ? `${g} — ` : ""}${d} (${e(t.status)})${l ? ", on critical path" : ""}`;
171
171
  return /* @__PURE__ */ o("g", {
172
172
  "data-testid": "dependency-graph-node",
173
173
  transform: `translate(${i.x}, ${i.y})`,
174
- ...t(() => C(n.id), _),
174
+ ...r(() => C(t.id), _),
175
175
  className: "cursor-pointer focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2",
176
176
  children: [
177
177
  /* @__PURE__ */ a("title", { children: _ }),
@@ -186,7 +186,7 @@ function y({ nodes: y, edges: b, criticalNodeIds: x, criticalEdgeIds: S, onActiv
186
186
  cx: 16,
187
187
  cy: c / 2,
188
188
  r: f,
189
- className: u.fill
189
+ style: { fill: u.color }
190
190
  }),
191
191
  /* @__PURE__ */ a("text", {
192
192
  x: 28,
@@ -201,7 +201,7 @@ function y({ nodes: y, edges: b, criticalNodeIds: x, criticalEdgeIds: S, onActiv
201
201
  children: h(d, p)
202
202
  })
203
203
  ]
204
- }, n.id);
204
+ }, t.id);
205
205
  })
206
206
  ]
207
207
  })
@@ -210,7 +210,10 @@ function y({ nodes: y, edges: b, criticalNodeIds: x, criticalEdgeIds: S, onActiv
210
210
  className: "flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-input px-3 py-2 text-[11px] text-muted-foreground",
211
211
  children: [D.map((t) => /* @__PURE__ */ o("span", {
212
212
  className: "inline-flex items-center gap-1.5",
213
- children: [/* @__PURE__ */ a("span", { className: `h-2.5 w-2.5 rounded-full ${r(t).swatch}` }), e(t)]
213
+ children: [/* @__PURE__ */ a("span", {
214
+ className: "h-2.5 w-2.5 rounded-full",
215
+ style: { backgroundColor: n(t).color }
216
+ }), e(t)]
214
217
  }, t)), x && x.size > 0 && /* @__PURE__ */ o("span", {
215
218
  className: "inline-flex items-center gap-1.5",
216
219
  children: [/* @__PURE__ */ a("span", { className: "h-2.5 w-2.5 rounded-full border-2 border-primary bg-background" }), "Critical path"]
@@ -1 +1 @@
1
- {"version":3,"file":"DependencyGraph.js","names":[],"sources":["../../../src/planmagnet/dependency/DependencyGraph.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { statusVisual, STATUS_LEGEND_ORDER } from '../utils/statusColor';\n\n/**\n * Reusable SVG dependency graph (PDM-27). Nodes are work items, edges are `WorkItemDependency` rows, laid out in\n * layers left-to-right by longest-path topological order. Two mount points share this component:\n * - the critical-path page's \"Graph\" view (the whole project — see useProjectWorkItemsGraph.ts)\n * - the task dependency panel's one-hop neighbourhood graph (this item, its predecessors, its successors)\n *\n * The existing text list stays the accessible/no-JS-heavy fallback at every call site — this component is an\n * additional view, never a replacement for it.\n */\n\nexport interface DependencyGraphNode {\n id: string;\n key: string | null;\n title: string | null;\n status: string;\n}\n\nexport interface DependencyGraphEdge {\n /** The WorkItemDependency's own id — stable per edge, used as the React key. */\n id: string;\n predecessorId: string;\n successorId: string;\n /** A DependencyType value. */\n type: string;\n lagMinutes: number | null;\n}\n\nexport interface DependencyGraphProps {\n nodes: DependencyGraphNode[];\n edges: DependencyGraphEdge[];\n /** Node ids on the critical path (CPM chain) — emphasised with a thicker primary outline. */\n criticalNodeIds?: ReadonlySet<string>;\n /** Edge ids that connect two consecutive critical-path nodes — emphasised the same way. */\n criticalEdgeIds?: ReadonlySet<string>;\n onActivateNode: (nodeId: string) => void;\n /** Shown instead of the graph when `nodes` is empty. */\n emptyMessage: string;\n /** Accessible name for the graph region — should say what project/item it scopes to. */\n ariaLabel: string;\n}\n\nconst NODE_WIDTH = 180;\nconst NODE_HEIGHT = 56;\nconst LAYER_GAP_X = 72;\nconst NODE_GAP_Y = 16;\nconst PADDING = 20;\nconst CHIP_R = 4;\nconst TITLE_MAX_CHARS = 24;\nconst KEY_MAX_CHARS = 16;\n\n/** Shared with GanttChart.tsx, which draws the same dependency edges between bars instead of nodes. */\nexport function truncate(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n return `${text.slice(0, Math.max(1, maxChars - 1))}…`;\n}\n\nexport function dependencyTypeLabel(type: string): string {\n switch (type) {\n case 'FINISH_TO_START':\n return 'Finish to start';\n case 'START_TO_START':\n return 'Start to start';\n case 'FINISH_TO_FINISH':\n return 'Finish to finish';\n case 'START_TO_FINISH':\n return 'Start to finish';\n default:\n return enumLabel(type);\n }\n}\n\nexport function lagLabel(lagMinutes: number | null): string | null {\n if (!lagMinutes) return null;\n const days = lagMinutes / (60 * 24);\n const verb = lagMinutes > 0 ? 'lag' : 'lead';\n if (Number.isInteger(days)) return `${Math.abs(days)}d ${verb}`;\n return `${Math.abs(Math.round((lagMinutes / 60) * 10) / 10)}h ${verb}`;\n}\n\n/**\n * Longest-path layering with a cycle guard. Every node ends up on the layer one past the FURTHEST-along of its\n * (in-graph) predecessors — never merely the first predecessor visited — so an edge never points backward across\n * layers except when a cycle forces it (the server refuses cycles; this is defensive, not the expected path).\n *\n * `resolving` tracks ids currently on the call stack: a predecessor already being resolved (a back-edge closing a\n * cycle) is treated as layer -1 (contributing 0) rather than recursed into again, so this always terminates —\n * every id is visited at most once per still-active ancestor chain, bounded by `nodeIds.length`.\n */\nfunction computeLayers(\n nodeIds: string[],\n edges: { predecessorId: string; successorId: string }[]\n): Map<string, number> {\n const predecessorsOf = new Map<string, string[]>(nodeIds.map((id) => [id, []]));\n for (const e of edges) {\n const preds = predecessorsOf.get(e.successorId);\n if (preds && predecessorsOf.has(e.predecessorId)) preds.push(e.predecessorId);\n }\n\n const layer = new Map<string, number>();\n const resolving = new Set<string>();\n\n function resolve(id: string, depth: number): number {\n const cached = layer.get(id);\n if (cached !== undefined) return cached;\n if (resolving.has(id) || depth > nodeIds.length) return -1;\n resolving.add(id);\n const preds = predecessorsOf.get(id) ?? [];\n let max = -1;\n for (const p of preds) max = Math.max(max, resolve(p, depth + 1));\n resolving.delete(id);\n const result = max + 1;\n layer.set(id, result);\n return result;\n }\n\n for (const id of nodeIds) resolve(id, 0);\n return layer;\n}\n\nexport function DependencyGraph({\n nodes,\n edges,\n criticalNodeIds,\n criticalEdgeIds,\n onActivateNode,\n emptyMessage,\n ariaLabel,\n}: DependencyGraphProps) {\n const layout = useMemo(() => {\n const nodeIds = nodes.map((n) => n.id);\n const nodeSet = new Set(nodeIds);\n // Cross-scope edges (e.g. a dependency on a work item outside this project, or outside this item's one-hop\n // neighbourhood) have no node to draw — drop them rather than crash on a missing endpoint.\n const inScopeEdges = edges.filter(\n (e) => nodeSet.has(e.predecessorId) && nodeSet.has(e.successorId)\n );\n const layerOf = computeLayers(nodeIds, inScopeEdges);\n\n const byLayer = new Map<number, string[]>();\n for (const id of nodeIds) {\n const l = layerOf.get(id) ?? 0;\n const list = byLayer.get(l);\n if (list) list.push(id);\n else byLayer.set(l, [id]);\n }\n const layerCount = byLayer.size === 0 ? 1 : Math.max(...byLayer.keys()) + 1;\n const maxPerLayer = Math.max(1, ...Array.from(byLayer.values(), (l) => l.length));\n\n const position = new Map<string, { x: number; y: number }>();\n for (const [l, ids] of byLayer) {\n const x = PADDING + l * (NODE_WIDTH + LAYER_GAP_X);\n // Centered vertically within the tallest layer, so a short layer doesn't hug the top.\n const layerHeight = ids.length * NODE_HEIGHT + (ids.length - 1) * NODE_GAP_Y;\n const fullHeight = maxPerLayer * NODE_HEIGHT + (maxPerLayer - 1) * NODE_GAP_Y;\n const yOffset = PADDING + Math.max(0, (fullHeight - layerHeight) / 2);\n ids.forEach((id, i) => {\n position.set(id, { x, y: yOffset + i * (NODE_HEIGHT + NODE_GAP_Y) });\n });\n }\n\n const width = PADDING * 2 + layerCount * NODE_WIDTH + Math.max(0, layerCount - 1) * LAYER_GAP_X;\n const height =\n PADDING * 2 + maxPerLayer * NODE_HEIGHT + Math.max(0, maxPerLayer - 1) * NODE_GAP_Y;\n\n return { position, inScopeEdges, width, height };\n }, [nodes, edges]);\n\n const presentStatuses = useMemo(\n () => STATUS_LEGEND_ORDER.filter((s) => nodes.some((n) => n.status === s)),\n [nodes]\n );\n\n if (nodes.length === 0) {\n return (\n <div data-testid=\"dependency-graph\" className=\"p-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n );\n }\n\n const nodesById = new Map(nodes.map((n) => [n.id, n]));\n\n return (\n <div data-testid=\"dependency-graph\" className=\"rounded-md border border-input bg-background\">\n <p className=\"sr-only\">\n Dependency graph with {nodes.length} work item{nodes.length === 1 ? '' : 's'} and{' '}\n {layout.inScopeEdges.length} dependenc{layout.inScopeEdges.length === 1 ? 'y' : 'ies'}.\n {criticalNodeIds && criticalNodeIds.size > 0\n ? ` ${criticalNodeIds.size} item${criticalNodeIds.size === 1 ? ' is' : 's are'} on the critical path.`\n : ''}{' '}\n The list above/below gives the same information as text.\n </p>\n <div className=\"overflow-x-auto p-3\">\n {/* No role=\"img\" here: each node below is its own focusable role=\"button\" descendant, and \"img\"\n semantics would tell assistive tech to treat the whole subtree as a single flattened image. */}\n <svg\n aria-label={ariaLabel}\n width={layout.width}\n height={layout.height}\n viewBox={`0 0 ${layout.width} ${layout.height}`}\n className=\"block\"\n >\n <defs>\n <marker\n id=\"dependency-graph-arrow\"\n viewBox=\"0 0 10 10\"\n refX=\"9\"\n refY=\"5\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n className=\"text-border-strong\"\n >\n <path d=\"M0,0 L10,5 L0,10 z\" fill=\"currentColor\" />\n </marker>\n <marker\n id=\"dependency-graph-arrow-critical\"\n viewBox=\"0 0 10 10\"\n refX=\"9\"\n refY=\"5\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n className=\"text-primary\"\n >\n <path d=\"M0,0 L10,5 L0,10 z\" fill=\"currentColor\" />\n </marker>\n </defs>\n\n {layout.inScopeEdges.map((edge) => {\n const from = layout.position.get(edge.predecessorId);\n const to = layout.position.get(edge.successorId);\n if (!from || !to) return null;\n const x1 = from.x + NODE_WIDTH;\n const y1 = from.y + NODE_HEIGHT / 2;\n const x2 = to.x;\n const y2 = to.y + NODE_HEIGHT / 2;\n const midX = (x1 + x2) / 2;\n const critical = criticalEdgeIds?.has(edge.id) ?? false;\n const typeLabel =\n edge.type === 'FINISH_TO_START' ? null : dependencyTypeLabel(edge.type);\n const lag = lagLabel(edge.lagMinutes);\n const label = [typeLabel, lag].filter((v): v is string => !!v).join(' · ');\n const predecessor = nodesById.get(edge.predecessorId);\n const successor = nodesById.get(edge.successorId);\n return (\n <g key={edge.id} data-testid=\"dependency-graph-edge\">\n <title>\n {predecessor?.key ?? edge.predecessorId} {dependencyTypeLabel(edge.type)}{' '}\n {successor?.key ?? edge.successorId}\n {lag ? ` (${lag})` : ''}\n </title>\n <path\n d={`M ${x1},${y1} C ${midX},${y1} ${midX},${y2} ${x2},${y2}`}\n fill=\"none\"\n strokeWidth={critical ? 2.5 : 1.5}\n className={critical ? 'text-primary' : 'text-border-strong'}\n stroke=\"currentColor\"\n markerEnd={`url(#${critical ? 'dependency-graph-arrow-critical' : 'dependency-graph-arrow'})`}\n />\n {label && (\n <text\n x={midX}\n y={(y1 + y2) / 2 - 4}\n textAnchor=\"middle\"\n className=\"fill-muted-foreground text-[9px]\"\n >\n {label}\n </text>\n )}\n </g>\n );\n })}\n\n {nodes.map((node) => {\n const pos = layout.position.get(node.id);\n if (!pos) return null;\n const critical = criticalNodeIds?.has(node.id) ?? false;\n const visual = statusVisual(node.status);\n const title = node.title ?? 'Untitled work item';\n const keyLabel = node.key ?? '';\n const accessibleLabel = `${keyLabel ? `${keyLabel} — ` : ''}${title} (${enumLabel(node.status)})${critical ? ', on critical path' : ''}`;\n // ROW_LINK_CLASS (in the rowLinkProps spread below) rings via box-shadow, which SVG elements\n // outside a <foreignObject> don't paint — the explicit className after the spread overrides it\n // with an outline-based focus style, which they do.\n return (\n <g\n key={node.id}\n data-testid=\"dependency-graph-node\"\n transform={`translate(${pos.x}, ${pos.y})`}\n {...rowLinkProps<SVGGElement>(() => onActivateNode(node.id), accessibleLabel)}\n className=\"cursor-pointer focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2\"\n >\n <title>{accessibleLabel}</title>\n <rect\n width={NODE_WIDTH}\n height={NODE_HEIGHT}\n rx={8}\n className={`fill-background ${critical ? 'stroke-primary' : 'stroke-input'}`}\n strokeWidth={critical ? 2 : 1}\n />\n <circle cx={16} cy={NODE_HEIGHT / 2} r={CHIP_R} className={visual.fill} />\n <text x={28} y={22} className=\"fill-muted-foreground text-[10px] font-medium\">\n {truncate(keyLabel, KEY_MAX_CHARS)}\n </text>\n <text x={28} y={38} className=\"fill-foreground text-[11px] font-medium\">\n {truncate(title, TITLE_MAX_CHARS)}\n </text>\n </g>\n );\n })}\n </svg>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-input px-3 py-2 text-[11px] text-muted-foreground\">\n {presentStatuses.map((status) => (\n <span key={status} className=\"inline-flex items-center gap-1.5\">\n <span className={`h-2.5 w-2.5 rounded-full ${statusVisual(status).swatch}`} />\n {enumLabel(status)}\n </span>\n ))}\n {criticalNodeIds && criticalNodeIds.size > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full border-2 border-primary bg-background\" />\n Critical path\n </span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DependencyGraph.js","names":[],"sources":["../../../src/planmagnet/dependency/DependencyGraph.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { enumLabel } from '../controls/WorkItemControls';\nimport { rowLinkProps } from '../utils/rowNavigation';\nimport { statusVisual, STATUS_LEGEND_ORDER } from '../utils/statusColor';\n\n/**\n * Reusable SVG dependency graph (PDM-27). Nodes are work items, edges are `WorkItemDependency` rows, laid out in\n * layers left-to-right by longest-path topological order. Two mount points share this component:\n * - the critical-path page's \"Graph\" view (the whole project — see useProjectWorkItemsGraph.ts)\n * - the task dependency panel's one-hop neighbourhood graph (this item, its predecessors, its successors)\n *\n * The existing text list stays the accessible/no-JS-heavy fallback at every call site — this component is an\n * additional view, never a replacement for it.\n */\n\nexport interface DependencyGraphNode {\n id: string;\n key: string | null;\n title: string | null;\n status: string;\n}\n\nexport interface DependencyGraphEdge {\n /** The WorkItemDependency's own id — stable per edge, used as the React key. */\n id: string;\n predecessorId: string;\n successorId: string;\n /** A DependencyType value. */\n type: string;\n lagMinutes: number | null;\n}\n\nexport interface DependencyGraphProps {\n nodes: DependencyGraphNode[];\n edges: DependencyGraphEdge[];\n /** Node ids on the critical path (CPM chain) — emphasised with a thicker primary outline. */\n criticalNodeIds?: ReadonlySet<string>;\n /** Edge ids that connect two consecutive critical-path nodes — emphasised the same way. */\n criticalEdgeIds?: ReadonlySet<string>;\n onActivateNode: (nodeId: string) => void;\n /** Shown instead of the graph when `nodes` is empty. */\n emptyMessage: string;\n /** Accessible name for the graph region — should say what project/item it scopes to. */\n ariaLabel: string;\n}\n\nconst NODE_WIDTH = 180;\nconst NODE_HEIGHT = 56;\nconst LAYER_GAP_X = 72;\nconst NODE_GAP_Y = 16;\nconst PADDING = 20;\nconst CHIP_R = 4;\nconst TITLE_MAX_CHARS = 24;\nconst KEY_MAX_CHARS = 16;\n\n/** Shared with GanttChart.tsx, which draws the same dependency edges between bars instead of nodes. */\nexport function truncate(text: string, maxChars: number): string {\n if (text.length <= maxChars) return text;\n return `${text.slice(0, Math.max(1, maxChars - 1))}…`;\n}\n\nexport function dependencyTypeLabel(type: string): string {\n switch (type) {\n case 'FINISH_TO_START':\n return 'Finish to start';\n case 'START_TO_START':\n return 'Start to start';\n case 'FINISH_TO_FINISH':\n return 'Finish to finish';\n case 'START_TO_FINISH':\n return 'Start to finish';\n default:\n return enumLabel(type);\n }\n}\n\nexport function lagLabel(lagMinutes: number | null): string | null {\n if (!lagMinutes) return null;\n const days = lagMinutes / (60 * 24);\n const verb = lagMinutes > 0 ? 'lag' : 'lead';\n if (Number.isInteger(days)) return `${Math.abs(days)}d ${verb}`;\n return `${Math.abs(Math.round((lagMinutes / 60) * 10) / 10)}h ${verb}`;\n}\n\n/**\n * Longest-path layering with a cycle guard. Every node ends up on the layer one past the FURTHEST-along of its\n * (in-graph) predecessors — never merely the first predecessor visited — so an edge never points backward across\n * layers except when a cycle forces it (the server refuses cycles; this is defensive, not the expected path).\n *\n * `resolving` tracks ids currently on the call stack: a predecessor already being resolved (a back-edge closing a\n * cycle) is treated as layer -1 (contributing 0) rather than recursed into again, so this always terminates —\n * every id is visited at most once per still-active ancestor chain, bounded by `nodeIds.length`.\n */\nfunction computeLayers(\n nodeIds: string[],\n edges: { predecessorId: string; successorId: string }[]\n): Map<string, number> {\n const predecessorsOf = new Map<string, string[]>(nodeIds.map((id) => [id, []]));\n for (const e of edges) {\n const preds = predecessorsOf.get(e.successorId);\n if (preds && predecessorsOf.has(e.predecessorId)) preds.push(e.predecessorId);\n }\n\n const layer = new Map<string, number>();\n const resolving = new Set<string>();\n\n function resolve(id: string, depth: number): number {\n const cached = layer.get(id);\n if (cached !== undefined) return cached;\n if (resolving.has(id) || depth > nodeIds.length) return -1;\n resolving.add(id);\n const preds = predecessorsOf.get(id) ?? [];\n let max = -1;\n for (const p of preds) max = Math.max(max, resolve(p, depth + 1));\n resolving.delete(id);\n const result = max + 1;\n layer.set(id, result);\n return result;\n }\n\n for (const id of nodeIds) resolve(id, 0);\n return layer;\n}\n\nexport function DependencyGraph({\n nodes,\n edges,\n criticalNodeIds,\n criticalEdgeIds,\n onActivateNode,\n emptyMessage,\n ariaLabel,\n}: DependencyGraphProps) {\n const layout = useMemo(() => {\n const nodeIds = nodes.map((n) => n.id);\n const nodeSet = new Set(nodeIds);\n // Cross-scope edges (e.g. a dependency on a work item outside this project, or outside this item's one-hop\n // neighbourhood) have no node to draw — drop them rather than crash on a missing endpoint.\n const inScopeEdges = edges.filter(\n (e) => nodeSet.has(e.predecessorId) && nodeSet.has(e.successorId)\n );\n const layerOf = computeLayers(nodeIds, inScopeEdges);\n\n const byLayer = new Map<number, string[]>();\n for (const id of nodeIds) {\n const l = layerOf.get(id) ?? 0;\n const list = byLayer.get(l);\n if (list) list.push(id);\n else byLayer.set(l, [id]);\n }\n const layerCount = byLayer.size === 0 ? 1 : Math.max(...byLayer.keys()) + 1;\n const maxPerLayer = Math.max(1, ...Array.from(byLayer.values(), (l) => l.length));\n\n const position = new Map<string, { x: number; y: number }>();\n for (const [l, ids] of byLayer) {\n const x = PADDING + l * (NODE_WIDTH + LAYER_GAP_X);\n // Centered vertically within the tallest layer, so a short layer doesn't hug the top.\n const layerHeight = ids.length * NODE_HEIGHT + (ids.length - 1) * NODE_GAP_Y;\n const fullHeight = maxPerLayer * NODE_HEIGHT + (maxPerLayer - 1) * NODE_GAP_Y;\n const yOffset = PADDING + Math.max(0, (fullHeight - layerHeight) / 2);\n ids.forEach((id, i) => {\n position.set(id, { x, y: yOffset + i * (NODE_HEIGHT + NODE_GAP_Y) });\n });\n }\n\n const width = PADDING * 2 + layerCount * NODE_WIDTH + Math.max(0, layerCount - 1) * LAYER_GAP_X;\n const height =\n PADDING * 2 + maxPerLayer * NODE_HEIGHT + Math.max(0, maxPerLayer - 1) * NODE_GAP_Y;\n\n return { position, inScopeEdges, width, height };\n }, [nodes, edges]);\n\n const presentStatuses = useMemo(\n () => STATUS_LEGEND_ORDER.filter((s) => nodes.some((n) => n.status === s)),\n [nodes]\n );\n\n if (nodes.length === 0) {\n return (\n <div data-testid=\"dependency-graph\" className=\"p-6 text-center text-sm text-muted-foreground\">\n {emptyMessage}\n </div>\n );\n }\n\n const nodesById = new Map(nodes.map((n) => [n.id, n]));\n\n return (\n <div data-testid=\"dependency-graph\" className=\"rounded-md border border-input bg-background\">\n <p className=\"sr-only\">\n Dependency graph with {nodes.length} work item{nodes.length === 1 ? '' : 's'} and{' '}\n {layout.inScopeEdges.length} dependenc{layout.inScopeEdges.length === 1 ? 'y' : 'ies'}.\n {criticalNodeIds && criticalNodeIds.size > 0\n ? ` ${criticalNodeIds.size} item${criticalNodeIds.size === 1 ? ' is' : 's are'} on the critical path.`\n : ''}{' '}\n The list above/below gives the same information as text.\n </p>\n <div className=\"overflow-x-auto p-3\">\n {/* No role=\"img\" here: each node below is its own focusable role=\"button\" descendant, and \"img\"\n semantics would tell assistive tech to treat the whole subtree as a single flattened image. */}\n <svg\n aria-label={ariaLabel}\n width={layout.width}\n height={layout.height}\n viewBox={`0 0 ${layout.width} ${layout.height}`}\n className=\"block\"\n >\n <defs>\n <marker\n id=\"dependency-graph-arrow\"\n viewBox=\"0 0 10 10\"\n refX=\"9\"\n refY=\"5\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n className=\"text-border-strong\"\n >\n <path d=\"M0,0 L10,5 L0,10 z\" fill=\"currentColor\" />\n </marker>\n <marker\n id=\"dependency-graph-arrow-critical\"\n viewBox=\"0 0 10 10\"\n refX=\"9\"\n refY=\"5\"\n markerWidth=\"6\"\n markerHeight=\"6\"\n orient=\"auto-start-reverse\"\n className=\"text-primary\"\n >\n <path d=\"M0,0 L10,5 L0,10 z\" fill=\"currentColor\" />\n </marker>\n </defs>\n\n {layout.inScopeEdges.map((edge) => {\n const from = layout.position.get(edge.predecessorId);\n const to = layout.position.get(edge.successorId);\n if (!from || !to) return null;\n const x1 = from.x + NODE_WIDTH;\n const y1 = from.y + NODE_HEIGHT / 2;\n const x2 = to.x;\n const y2 = to.y + NODE_HEIGHT / 2;\n const midX = (x1 + x2) / 2;\n const critical = criticalEdgeIds?.has(edge.id) ?? false;\n const typeLabel =\n edge.type === 'FINISH_TO_START' ? null : dependencyTypeLabel(edge.type);\n const lag = lagLabel(edge.lagMinutes);\n const label = [typeLabel, lag].filter((v): v is string => !!v).join(' · ');\n const predecessor = nodesById.get(edge.predecessorId);\n const successor = nodesById.get(edge.successorId);\n return (\n <g key={edge.id} data-testid=\"dependency-graph-edge\">\n <title>\n {predecessor?.key ?? edge.predecessorId} {dependencyTypeLabel(edge.type)}{' '}\n {successor?.key ?? edge.successorId}\n {lag ? ` (${lag})` : ''}\n </title>\n <path\n d={`M ${x1},${y1} C ${midX},${y1} ${midX},${y2} ${x2},${y2}`}\n fill=\"none\"\n strokeWidth={critical ? 2.5 : 1.5}\n className={critical ? 'text-primary' : 'text-border-strong'}\n stroke=\"currentColor\"\n markerEnd={`url(#${critical ? 'dependency-graph-arrow-critical' : 'dependency-graph-arrow'})`}\n />\n {label && (\n <text\n x={midX}\n y={(y1 + y2) / 2 - 4}\n textAnchor=\"middle\"\n className=\"fill-muted-foreground text-[9px]\"\n >\n {label}\n </text>\n )}\n </g>\n );\n })}\n\n {nodes.map((node) => {\n const pos = layout.position.get(node.id);\n if (!pos) return null;\n const critical = criticalNodeIds?.has(node.id) ?? false;\n const visual = statusVisual(node.status);\n const title = node.title ?? 'Untitled work item';\n const keyLabel = node.key ?? '';\n const accessibleLabel = `${keyLabel ? `${keyLabel} — ` : ''}${title} (${enumLabel(node.status)})${critical ? ', on critical path' : ''}`;\n // ROW_LINK_CLASS (in the rowLinkProps spread below) rings via box-shadow, which SVG elements\n // outside a <foreignObject> don't paint — the explicit className after the spread overrides it\n // with an outline-based focus style, which they do.\n return (\n <g\n key={node.id}\n data-testid=\"dependency-graph-node\"\n transform={`translate(${pos.x}, ${pos.y})`}\n {...rowLinkProps<SVGGElement>(() => onActivateNode(node.id), accessibleLabel)}\n className=\"cursor-pointer focus:outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-primary focus-visible:outline-offset-2\"\n >\n <title>{accessibleLabel}</title>\n <rect\n width={NODE_WIDTH}\n height={NODE_HEIGHT}\n rx={8}\n className={`fill-background ${critical ? 'stroke-primary' : 'stroke-input'}`}\n strokeWidth={critical ? 2 : 1}\n />\n <circle cx={16} cy={NODE_HEIGHT / 2} r={CHIP_R} style={{ fill: visual.color }} />\n <text x={28} y={22} className=\"fill-muted-foreground text-[10px] font-medium\">\n {truncate(keyLabel, KEY_MAX_CHARS)}\n </text>\n <text x={28} y={38} className=\"fill-foreground text-[11px] font-medium\">\n {truncate(title, TITLE_MAX_CHARS)}\n </text>\n </g>\n );\n })}\n </svg>\n </div>\n <div className=\"flex flex-wrap items-center gap-x-4 gap-y-1 border-t border-input px-3 py-2 text-[11px] text-muted-foreground\">\n {presentStatuses.map((status) => (\n <span key={status} className=\"inline-flex items-center gap-1.5\">\n <span\n className=\"h-2.5 w-2.5 rounded-full\"\n style={{ backgroundColor: statusVisual(status).color }}\n />\n {enumLabel(status)}\n </span>\n ))}\n {criticalNodeIds && criticalNodeIds.size > 0 && (\n <span className=\"inline-flex items-center gap-1.5\">\n <span className=\"h-2.5 w-2.5 rounded-full border-2 border-primary bg-background\" />\n Critical path\n </span>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -2,8 +2,8 @@ import { usePlanMagnetNavigate as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { throwOnError as t } from "../utils/mutationResult.js";
3
3
  import { ErrorLine as n } from "../controls/ConfirmButton.js";
4
4
  import { enumLabel as r } from "../controls/WorkItemControls.js";
5
- import { WorkItemSearchSelect as i } from "../controls/Pickers.js";
6
- import { DependencyGraph as a } from "./DependencyGraph.js";
5
+ import { DependencyGraph as i } from "./DependencyGraph.js";
6
+ import { WorkItemSearchSelect as a } from "../controls/Pickers.js";
7
7
  import { useState as o } from "react";
8
8
  import { ArrowLeft as s, ArrowRight as c, Plus as l, X as u } from "lucide-react";
9
9
  import { gql as d } from "@apollo/client";
@@ -118,7 +118,7 @@ function D({ keyLabel: e, title: t, type: n, lagMinutes: i, onOpen: a, onRemove:
118
118
  })]
119
119
  });
120
120
  }
121
- function O({ workItemId: e, projectId: a, excludeIds: s, onDone: c }) {
121
+ function O({ workItemId: e, projectId: i, excludeIds: s, onDone: c }) {
122
122
  let [l, u] = o("blocks"), [d, p] = o(""), [_, v] = o("FINISH_TO_START"), [y, b] = o(""), [S, C] = o(null), [T, { loading: E }] = f(w, {
123
123
  refetchQueries: ["GetWorkItemDependencies", "GetProjectCriticalPath"],
124
124
  awaitRefetchQueries: !0
@@ -166,9 +166,9 @@ function O({ workItemId: e, projectId: a, excludeIds: s, onDone: c }) {
166
166
  children: "Blocked by…"
167
167
  })]
168
168
  }),
169
- /* @__PURE__ */ m(i, {
169
+ /* @__PURE__ */ m(a, {
170
170
  id: "dependency-other-work-item",
171
- projectId: a,
171
+ projectId: i,
172
172
  value: d,
173
173
  onChange: p,
174
174
  exclude: s
@@ -220,18 +220,18 @@ function O({ workItemId: e, projectId: a, excludeIds: s, onDone: c }) {
220
220
  ]
221
221
  });
222
222
  }
223
- function k({ workspaceId: r, workItemId: i, projectId: u }) {
223
+ function k({ workspaceId: r, workItemId: a, projectId: u }) {
224
224
  let d = e(), [x, w] = o(!1), [E, k] = o(null), { data: A, loading: j, error: M, refetch: N } = p(S, {
225
225
  variables: {
226
226
  workspaceId: r,
227
- workItemId: i
227
+ workItemId: a
228
228
  },
229
- skip: !r || !i
229
+ skip: !r || !a
230
230
  }), { data: P, error: F } = p(C, {
231
- variables: { id: i },
232
- skip: !i
231
+ variables: { id: a },
232
+ skip: !a
233
233
  }), [I, { loading: L }] = f(T, { refetchQueries: ["GetWorkItemDependencies", "GetProjectCriticalPath"] }), R = A?.blocks.items ?? [], z = A?.blockedBy.items ?? [], B = [
234
- i,
234
+ a,
235
235
  ...R.map((e) => e.successorId),
236
236
  ...z.map((e) => e.predecessorId)
237
237
  ], V = P?.planmagnetWorkItem ?? null, H = /* @__PURE__ */ new Map();
@@ -270,7 +270,7 @@ function k({ workspaceId: r, workItemId: i, projectId: u }) {
270
270
  className: "space-y-4",
271
271
  children: [
272
272
  x && /* @__PURE__ */ m(O, {
273
- workItemId: i,
273
+ workItemId: a,
274
274
  projectId: u,
275
275
  excludeIds: B,
276
276
  onDone: () => {
@@ -340,7 +340,7 @@ function k({ workspaceId: r, workItemId: i, projectId: u }) {
340
340
  V && (R.length > 0 || z.length > 0) && /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
341
341
  className: "mb-2 text-xs font-medium text-muted-foreground uppercase",
342
342
  children: "Neighbourhood graph"
343
- }), /* @__PURE__ */ m(a, {
343
+ }), /* @__PURE__ */ m(i, {
344
344
  nodes: Array.from(H.values()),
345
345
  edges: U,
346
346
  onActivateNode: (e) => d(`/tasks/task-detail-page?taskId=${encodeURIComponent(e)}`),
@@ -1 +1 @@
1
- {"version":3,"file":"GanttChart.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/dependency/GanttChart.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,2GAA2G;IAC3G,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,IAAI,CAAC;CACd;AAsED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,eAAe,EACf,IAAI,EACJ,aAAa,EACb,KAAK,GACN,EAAE,eAAe,2CA6SjB"}
1
+ {"version":3,"file":"GanttChart.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/dependency/GanttChart.tsx"],"names":[],"mappings":"AAKA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,YAAY,EAAE,eAAe,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,EAAE,CAAC;IACvB,+EAA+E;IAC/E,WAAW,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAClC,2GAA2G;IAC3G,eAAe,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACtC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IACvB,aAAa,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,4EAA4E;IAC5E,KAAK,CAAC,EAAE,IAAI,CAAC;CACd;AAsED,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,WAAW,EACX,eAAe,EACf,IAAI,EACJ,aAAa,EACb,KAAK,GACN,EAAE,eAAe,2CAmTjB"}
@@ -200,13 +200,14 @@ function T({ items: b, criticalIds: T, criticalEdgeIds: E, zoom: D, onActivateRo
200
200
  x2: A.x(e.day),
201
201
  y1: 0,
202
202
  y2: A.plotHeight,
203
- className: "text-chart-grid",
203
+ style: { color: "var(--color-chart-grid)" },
204
204
  stroke: "currentColor",
205
205
  strokeWidth: 1
206
206
  }), /* @__PURE__ */ s("text", {
207
207
  x: A.x(e.day) + 3,
208
208
  y: p - 10,
209
- className: "fill-chart-axis text-[10px]",
209
+ className: "text-[10px]",
210
+ style: { fill: "var(--color-chart-axis)" },
210
211
  children: e.label
211
212
  })] }, e.day)),
212
213
  /* @__PURE__ */ s("line", {
@@ -250,7 +251,8 @@ function T({ items: b, criticalIds: T, criticalEdgeIds: E, zoom: D, onActivateRo
250
251
  width: h,
251
252
  height: h,
252
253
  transform: `rotate(45 ${e} ${i})`,
253
- className: `${o.fill} ${a ? "stroke-primary" : "stroke-input"}`,
254
+ className: a ? "stroke-primary" : "stroke-input",
255
+ style: { fill: o.color },
254
256
  strokeWidth: a ? 2 : 1
255
257
  })]
256
258
  }, t.item.id);
@@ -264,7 +266,8 @@ function T({ items: b, criticalIds: T, criticalEdgeIds: E, zoom: D, onActivateRo
264
266
  width: _,
265
267
  height: m,
266
268
  rx: 4,
267
- className: `${o.fill} ${a ? "stroke-primary" : "stroke-input"}`,
269
+ className: a ? "stroke-primary" : "stroke-input",
270
+ style: { fill: o.color },
268
271
  strokeWidth: a ? 2 : 1
269
272
  })]
270
273
  }, t.item.id);
@@ -278,7 +281,10 @@ function T({ items: b, criticalIds: T, criticalEdgeIds: E, zoom: D, onActivateRo
278
281
  children: [
279
282
  j.map((t) => /* @__PURE__ */ c("span", {
280
283
  className: "inline-flex items-center gap-1.5",
281
- children: [/* @__PURE__ */ s("span", { className: `h-2.5 w-2.5 rounded-full ${n(t).swatch}` }), e(t)]
284
+ children: [/* @__PURE__ */ s("span", {
285
+ className: "h-2.5 w-2.5 rounded-full",
286
+ style: { backgroundColor: n(t).color }
287
+ }), e(t)]
282
288
  }, t)),
283
289
  /* @__PURE__ */ c("span", {
284
290
  className: "inline-flex items-center gap-1.5",