@burdenoff/microfe-planmagnet 2026.1001.1 → 2026.1001.2

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 (72) hide show
  1. package/dist/planmagnet/PlanMagnetRoot.d.ts.map +1 -1
  2. package/dist/planmagnet/PlanMagnetRoot.js +156 -150
  3. package/dist/planmagnet/PlanMagnetRoot.js.map +1 -1
  4. package/dist/planmagnet/PlanMagnetRoutes.d.ts.map +1 -1
  5. package/dist/planmagnet/PlanMagnetRoutes.js +19 -15
  6. package/dist/planmagnet/PlanMagnetRoutes.js.map +1 -1
  7. package/dist/planmagnet/comments/CommentThread.js +36 -36
  8. package/dist/planmagnet/comments/RoadmapComments.js +21 -21
  9. package/dist/planmagnet/controls/AutomationRuleControls.js +24 -24
  10. package/dist/planmagnet/controls/FeatureControls.js +60 -60
  11. package/dist/planmagnet/controls/SyncControls.js +59 -59
  12. package/dist/planmagnet/discussion/WorkItemDiscussionPanel.js +15 -15
  13. package/dist/planmagnet/docs/DocSuggestionsPanel.js +12 -12
  14. package/dist/planmagnet/docs/DocVersionCompare.js +3 -3
  15. package/dist/planmagnet/forecast/DeliveryForecastCard.d.ts +12 -0
  16. package/dist/planmagnet/forecast/DeliveryForecastCard.d.ts.map +1 -0
  17. package/dist/planmagnet/forecast/DeliveryForecastCard.js +208 -0
  18. package/dist/planmagnet/forecast/DeliveryForecastCard.js.map +1 -0
  19. package/dist/planmagnet/forecast/ProjectOutlookCard.d.ts +5 -0
  20. package/dist/planmagnet/forecast/ProjectOutlookCard.d.ts.map +1 -0
  21. package/dist/planmagnet/forecast/ProjectOutlookCard.js +190 -0
  22. package/dist/planmagnet/forecast/ProjectOutlookCard.js.map +1 -0
  23. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.d.ts +5 -0
  24. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.d.ts.map +1 -0
  25. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js +99 -0
  26. package/dist/planmagnet/forecast/ProjectRiskSummaryCard.js.map +1 -0
  27. package/dist/planmagnet/forecast/ResourceForecastCard.d.ts +8 -0
  28. package/dist/planmagnet/forecast/ResourceForecastCard.d.ts.map +1 -0
  29. package/dist/planmagnet/forecast/ResourceForecastCard.js +185 -0
  30. package/dist/planmagnet/forecast/ResourceForecastCard.js.map +1 -0
  31. package/dist/planmagnet/forecast/riskFlags.d.ts +35 -0
  32. package/dist/planmagnet/forecast/riskFlags.d.ts.map +1 -0
  33. package/dist/planmagnet/forecast/riskFlags.js +37 -0
  34. package/dist/planmagnet/forecast/riskFlags.js.map +1 -0
  35. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
  36. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js +216 -182
  37. package/dist/planmagnet/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  38. package/dist/planmagnet/pages/AutomationAutomationRuleDetailPagePage.js +11 -11
  39. package/dist/planmagnet/pages/AutomationCreateAutomationRulePagePage.js +4 -4
  40. package/dist/planmagnet/pages/DocsDocDetailPagePage.js +163 -163
  41. package/dist/planmagnet/pages/DocsDocDetailPagePage.js.map +1 -1
  42. package/dist/planmagnet/pages/IntegrationsNewSyncPagePage.js +165 -165
  43. package/dist/planmagnet/pages/IntegrationsNewSyncPagePage.js.map +1 -1
  44. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.d.ts +2 -0
  45. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.d.ts.map +1 -0
  46. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js +234 -0
  47. package/dist/planmagnet/pages/ProjectsAtRiskPagePage.js.map +1 -0
  48. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.d.ts.map +1 -1
  49. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js +206 -182
  50. package/dist/planmagnet/pages/ProjectsProjectDetailPagePage.js.map +1 -1
  51. package/dist/planmagnet/pages/ReportsReportPagePage.js +22 -22
  52. package/dist/planmagnet/pages/ReportsReportPagePage.js.map +1 -1
  53. package/dist/planmagnet/pages/ReportsReportsPagePage.js +21 -21
  54. package/dist/planmagnet/pages/TeamTeamDetailPagePage.d.ts.map +1 -1
  55. package/dist/planmagnet/pages/TeamTeamDetailPagePage.js +88 -83
  56. package/dist/planmagnet/pages/TeamTeamDetailPagePage.js.map +1 -1
  57. package/dist/planmagnet/pages/analytics/SprintDeliveryCards.d.ts.map +1 -1
  58. package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js +139 -112
  59. package/dist/planmagnet/pages/analytics/SprintDeliveryCards.js.map +1 -1
  60. package/dist/planmagnet/pages/analytics/StatusFlowCard.js +21 -21
  61. package/dist/planmagnet/product/ProductControls.js +91 -91
  62. package/dist/planmagnet/reports/SchedulesPanel.js +33 -33
  63. package/dist/planmagnet/reports/ShareDialog.js +20 -20
  64. package/dist/planmagnet/reports/ShareDialog.js.map +1 -1
  65. package/dist/planmagnet/reports/sharedReportRoute.d.ts +3 -2
  66. package/dist/planmagnet/reports/sharedReportRoute.d.ts.map +1 -1
  67. package/dist/planmagnet/reports/sharedReportRoute.js.map +1 -1
  68. package/dist/planmagnet/roadmaps/RoadmapControls.js +60 -60
  69. package/dist/planmagnet/roadmaps/RoadmapNowNextLater.js +30 -30
  70. package/dist/planmagnet/roadmaps/RoadmapReviewPanel.js +21 -21
  71. package/dist/planmagnet/roadmaps/RoadmapVersionsPanel.js +22 -22
  72. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  import { usePlanMagnet as e } from "../providers/PlanMagnetProvider.js";
2
2
  import { PersonName as t } from "../people/People.js";
3
- import { throwOnError as n } from "../utils/mutationResult.js";
4
- import { ConfirmButton as r, ErrorLine as i } from "../controls/ConfirmButton.js";
5
- import { mutationErrorMessage as a } from "../utils/graphqlErrors.js";
3
+ import { mutationErrorMessage as n } from "../utils/graphqlErrors.js";
4
+ import { throwOnError as r } from "../utils/mutationResult.js";
5
+ import { ConfirmButton as i, ErrorLine as a } from "../controls/ConfirmButton.js";
6
6
  import { CREATE_REPORT_SCHEDULE as o, DELETE_REPORT_SCHEDULE as s, LIST_REPORT_SCHEDULES as c, UPDATE_REPORT_SCHEDULE as l } from "./reportGraphql.js";
7
7
  import { useDeferredValue as u, useEffect as d, useState as f } from "react";
8
8
  import { CalendarClock as p, Plus as m } from "lucide-react";
@@ -106,7 +106,7 @@ function W(e) {
106
106
  recipientIds: /* @__PURE__ */ new Set()
107
107
  };
108
108
  }
109
- function G({ reportId: e, schedule: t, onCancel: r, onSaved: s }) {
109
+ function G({ reportId: e, schedule: t, onCancel: i, onSaved: s }) {
110
110
  let [u, d] = f(() => W(t)), [p, m] = f(null), g = { refetchQueries: [{
111
111
  query: c,
112
112
  variables: { reportId: e }
@@ -118,7 +118,7 @@ function G({ reportId: e, schedule: t, onCancel: r, onSaved: s }) {
118
118
  };
119
119
  }), H = async () => {
120
120
  m(null);
121
- let r = {
121
+ let i = {
122
122
  cadence: u.cadence,
123
123
  hourUtc: u.hourUtc,
124
124
  dayOfWeek: u.cadence === "WEEKLY" ? u.dayOfWeek : void 0,
@@ -127,15 +127,15 @@ function G({ reportId: e, schedule: t, onCancel: r, onSaved: s }) {
127
127
  recipientIds: [...u.recipientIds]
128
128
  };
129
129
  try {
130
- t ? await n(T({ variables: {
130
+ t ? await r(T({ variables: {
131
131
  id: t.id,
132
- input: r
133
- } })) : await n(b({ variables: {
132
+ input: i
133
+ } })) : await r(b({ variables: {
134
134
  reportId: e,
135
- input: r
135
+ input: i
136
136
  } })), s();
137
137
  } catch (e) {
138
- m(a(e, "The schedule could not be saved."));
138
+ m(n(e, "The schedule could not be saved."));
139
139
  }
140
140
  };
141
141
  return /* @__PURE__ */ y(_, { children: [
@@ -259,10 +259,10 @@ function G({ reportId: e, schedule: t, onCancel: r, onSaved: s }) {
259
259
  })] })
260
260
  ]
261
261
  }),
262
- p && /* @__PURE__ */ v(i, { message: p }),
262
+ p && /* @__PURE__ */ v(a, { message: p }),
263
263
  /* @__PURE__ */ y(O, { children: [/* @__PURE__ */ v(x, {
264
264
  variant: "outline",
265
- onClick: r,
265
+ onClick: i,
266
266
  children: "Cancel"
267
267
  }), /* @__PURE__ */ v(x, {
268
268
  onClick: () => void H(),
@@ -287,7 +287,7 @@ function K({ reportId: e, schedule: t, open: n, onOpenChange: r }) {
287
287
  });
288
288
  }
289
289
  function q({ schedule: e, reportId: t }) {
290
- let [r, { loading: o }] = h(l, { refetchQueries: [{
290
+ let [i, { loading: o }] = h(l, { refetchQueries: [{
291
291
  query: c,
292
292
  variables: { reportId: t }
293
293
  }] }), [s, u] = f(null);
@@ -301,12 +301,12 @@ function q({ schedule: e, reportId: t }) {
301
301
  onCheckedChange: (t) => {
302
302
  u(null), (async () => {
303
303
  try {
304
- await n(r({ variables: {
304
+ await r(i({ variables: {
305
305
  id: e.id,
306
306
  input: { active: t }
307
307
  } }));
308
308
  } catch (e) {
309
- u(a(e, "Could not update."));
309
+ u(n(e, "Could not update."));
310
310
  }
311
311
  })();
312
312
  }
@@ -315,7 +315,7 @@ function q({ schedule: e, reportId: t }) {
315
315
  className: "text-xs text-muted-foreground",
316
316
  children: e.active ? "Active" : "Paused"
317
317
  }),
318
- s && /* @__PURE__ */ v(i, { message: s })
318
+ s && /* @__PURE__ */ v(a, { message: s })
319
319
  ]
320
320
  });
321
321
  }
@@ -345,7 +345,7 @@ function J({ reportId: e }) {
345
345
  className: "text-xs text-muted-foreground",
346
346
  children: "Email delivery is not available yet (Linear BOFF-7927) — recipients read a scheduled run in-app, under run history."
347
347
  }),
348
- T && /* @__PURE__ */ v(i, { message: T }),
348
+ T && /* @__PURE__ */ v(a, { message: T }),
349
349
  C && !S ? /* @__PURE__ */ v("p", {
350
350
  className: "text-sm text-muted-foreground",
351
351
  children: "Loading…"
@@ -354,7 +354,7 @@ function J({ reportId: e }) {
354
354
  children: "No schedules yet."
355
355
  }) : /* @__PURE__ */ v("ul", {
356
356
  className: "space-y-2",
357
- children: D.map((i) => /* @__PURE__ */ y("li", {
357
+ children: D.map((a) => /* @__PURE__ */ y("li", {
358
358
  className: "rounded-md border border-border p-3 text-sm",
359
359
  children: [
360
360
  /* @__PURE__ */ y("div", {
@@ -369,36 +369,36 @@ function J({ reportId: e }) {
369
369
  /* @__PURE__ */ y("span", {
370
370
  className: "font-medium",
371
371
  children: [
372
- i.cadence.charAt(0) + i.cadence.slice(1).toLowerCase(),
372
+ a.cadence.charAt(0) + a.cadence.slice(1).toLowerCase(),
373
373
  " at",
374
374
  " ",
375
- String(i.hourUtc).padStart(2, "0"),
375
+ String(a.hourUtc).padStart(2, "0"),
376
376
  ":00 UTC (",
377
- V(i.hourUtc),
377
+ V(a.hourUtc),
378
378
  ")"
379
379
  ]
380
380
  }),
381
381
  /* @__PURE__ */ v(b, {
382
382
  variant: "outline",
383
- children: i.format
383
+ children: a.format
384
384
  })
385
385
  ]
386
386
  }), /* @__PURE__ */ y("div", {
387
387
  className: "flex items-center gap-2",
388
388
  children: [
389
389
  /* @__PURE__ */ v(q, {
390
- schedule: i,
390
+ schedule: a,
391
391
  reportId: e
392
392
  }),
393
393
  /* @__PURE__ */ v(x, {
394
394
  size: "sm",
395
395
  variant: "outline",
396
396
  onClick: () => {
397
- d(i), l(!0);
397
+ d(a), l(!0);
398
398
  },
399
399
  children: "Edit"
400
400
  }),
401
- /* @__PURE__ */ v(r, {
401
+ /* @__PURE__ */ v(i, {
402
402
  label: "Delete",
403
403
  icon: null,
404
404
  title: "Delete this schedule?",
@@ -410,9 +410,9 @@ function J({ reportId: e }) {
410
410
  onConfirm: () => {
411
411
  E(null), (async () => {
412
412
  try {
413
- await n(w({ variables: { id: i.id } }));
413
+ await r(w({ variables: { id: a.id } }));
414
414
  } catch (e) {
415
- E(a(e, "Could not delete."));
415
+ E(n(e, "Could not delete."));
416
416
  }
417
417
  })();
418
418
  }
@@ -424,23 +424,23 @@ function J({ reportId: e }) {
424
424
  className: "mt-1 text-xs text-muted-foreground",
425
425
  children: [
426
426
  "Next run ",
427
- H(i.nextRunAt),
427
+ H(a.nextRunAt),
428
428
  " · Last run",
429
429
  " ",
430
- H(i.lastRunAt),
431
- i.lastError ? ` · last error: ${i.lastError}` : ""
430
+ H(a.lastRunAt),
431
+ a.lastError ? ` · last error: ${a.lastError}` : ""
432
432
  ]
433
433
  }),
434
434
  /* @__PURE__ */ v("p", {
435
435
  className: "mt-1 text-xs text-muted-foreground",
436
- children: i.recipientIds.length === 0 ? "No recipients" : /* @__PURE__ */ y(_, { children: [
436
+ children: a.recipientIds.length === 0 ? "No recipients" : /* @__PURE__ */ y(_, { children: [
437
437
  "Recipients:",
438
438
  " ",
439
- i.recipientIds.map((e, n) => /* @__PURE__ */ y("span", { children: [n > 0 && ", ", /* @__PURE__ */ v(t, { id: e })] }, e))
439
+ a.recipientIds.map((e, n) => /* @__PURE__ */ y("span", { children: [n > 0 && ", ", /* @__PURE__ */ v(t, { id: e })] }, e))
440
440
  ] })
441
441
  })
442
442
  ]
443
- }, i.id))
443
+ }, a.id))
444
444
  }),
445
445
  /* @__PURE__ */ v(K, {
446
446
  reportId: e,
@@ -1,13 +1,13 @@
1
- import { throwOnError as e } from "../utils/mutationResult.js";
2
- import { ConfirmButton as t, ErrorLine as n } from "../controls/ConfirmButton.js";
3
- import { mutationErrorMessage as r } from "../utils/graphqlErrors.js";
1
+ import { mutationErrorMessage as e } from "../utils/graphqlErrors.js";
2
+ import { throwOnError as t } from "../utils/mutationResult.js";
3
+ import { ConfirmButton as n, ErrorLine as r } from "../controls/ConfirmButton.js";
4
4
  import { sharedReportLink as i } from "./sharedReportRoute.js";
5
5
  import { CREATE_REPORT_SHARE as a, LIST_REPORT_SHARES as o, REVOKE_REPORT_SHARE as s } from "./reportGraphql.js";
6
6
  import { useState as c } from "react";
7
7
  import { Check as l, Copy as u, Share2 as d } from "lucide-react";
8
8
  import { useMutation as f, useQuery as p } from "@apollo/client/react";
9
9
  import { jsx as m, jsxs as h } from "react/jsx-runtime";
10
- import { Badge as g, Button as _, Input as v, Label as y, ResponsiveDialog as b, ResponsiveDialogContent as ee, ResponsiveDialogDescription as x, ResponsiveDialogFooter as S, ResponsiveDialogHeader as C, ResponsiveDialogTitle as w, ResponsiveDialogTrigger as T } from "@burdenoff/fe-libs/ui";
10
+ import { Badge as g, Button as _, Input as v, Label as y, ResponsiveDialog as b, ResponsiveDialogContent as x, ResponsiveDialogDescription as S, ResponsiveDialogFooter as C, ResponsiveDialogHeader as ee, ResponsiveDialogTitle as w, ResponsiveDialogTrigger as T } from "@burdenoff/fe-libs/ui";
11
11
  import { copyTextToClipboard as E } from "@burdenoff/fe-libs/sharing";
12
12
  //#region src/planmagnet/reports/ShareDialog.tsx
13
13
  var D = 864e5, O = 7, k = 90;
@@ -31,7 +31,7 @@ function M({ reportId: D, runId: M }) {
31
31
  if (M) {
32
32
  H(null), W(null);
33
33
  try {
34
- let t = (await e(J({ variables: {
34
+ let e = (await t(J({ variables: {
35
35
  runId: M,
36
36
  input: {
37
37
  expiresAt: `${F}T23:59:59.999Z`,
@@ -39,17 +39,17 @@ function M({ reportId: D, runId: M }) {
39
39
  watermark: z.trim() || void 0
40
40
  }
41
41
  } }))).data?.planmagnetCreateReportShare;
42
- t && (W(t), K(!1), R(""), B(""));
43
- } catch (e) {
44
- H(r(e, "The share link could not be created."));
42
+ e && (W(e), K(!1), R(""), B(""));
43
+ } catch (t) {
44
+ H(e(t, "The share link could not be created."));
45
45
  }
46
46
  }
47
- }, Q = async (t) => {
47
+ }, Q = async (n) => {
48
48
  H(null);
49
49
  try {
50
- await e(X({ variables: { id: t } })), U?.id === t && W(null);
51
- } catch (e) {
52
- H(r(e, "The share could not be revoked."));
50
+ await t(X({ variables: { id: n } })), U?.id === n && W(null);
51
+ } catch (t) {
52
+ H(e(t, "The share could not be revoked."));
53
53
  }
54
54
  }, te = async (e) => {
55
55
  try {
@@ -71,10 +71,10 @@ function M({ reportId: D, runId: M }) {
71
71
  "data-testid": "report-share",
72
72
  children: [/* @__PURE__ */ m(d, { className: "mr-1 h-4 w-4" }), "Share"]
73
73
  })
74
- }), /* @__PURE__ */ h(ee, {
74
+ }), /* @__PURE__ */ h(x, {
75
75
  className: "max-h-[85vh] overflow-y-auto sm:max-w-lg",
76
76
  children: [
77
- /* @__PURE__ */ h(C, { children: [/* @__PURE__ */ m(w, { children: "Share this report" }), /* @__PURE__ */ m(x, { children: "Anyone with the link can view the most recently shared run, read-only, without signing in." })] }),
77
+ /* @__PURE__ */ h(ee, { children: [/* @__PURE__ */ m(w, { children: "Share this report" }), /* @__PURE__ */ m(S, { children: "Anyone with the link can view the most recently shared run, read-only, without signing in." })] }),
78
78
  !M && /* @__PURE__ */ m("p", {
79
79
  className: "text-sm text-muted-foreground",
80
80
  children: "Run this report at least once before sharing it — a share links to one specific run."
@@ -151,7 +151,7 @@ function M({ reportId: D, runId: M }) {
151
151
  })
152
152
  ]
153
153
  }),
154
- V && /* @__PURE__ */ m(n, { message: V }),
154
+ V && /* @__PURE__ */ m(r, { message: V }),
155
155
  /* @__PURE__ */ h("div", {
156
156
  className: "space-y-2",
157
157
  "data-testid": "report-shares",
@@ -167,7 +167,7 @@ function M({ reportId: D, runId: M }) {
167
167
  }) : /* @__PURE__ */ m("ul", {
168
168
  className: "space-y-2",
169
169
  children: $.map((e) => {
170
- let n = !!e.revokedAt, r = !n && new Date(e.expiresAt).getTime() < (/* @__PURE__ */ new Date()).getTime();
170
+ let t = !!e.revokedAt, r = !t && new Date(e.expiresAt).getTime() < (/* @__PURE__ */ new Date()).getTime();
171
171
  return /* @__PURE__ */ h("li", {
172
172
  className: "flex flex-wrap items-center justify-between gap-2 rounded-md border border-border p-2 text-xs",
173
173
  children: [/* @__PURE__ */ h("div", {
@@ -176,8 +176,8 @@ function M({ reportId: D, runId: M }) {
176
176
  /* @__PURE__ */ h("div", {
177
177
  className: "flex items-center gap-1.5",
178
178
  children: [/* @__PURE__ */ m(g, {
179
- variant: n ? "secondary" : r ? "warning" : "success",
180
- children: n ? "Revoked" : r ? "Expired" : "Active"
179
+ variant: t ? "secondary" : r ? "warning" : "success",
180
+ children: t ? "Revoked" : r ? "Expired" : "Active"
181
181
  }), e.hasPassword && /* @__PURE__ */ m(g, {
182
182
  variant: "outline",
183
183
  children: "Password"
@@ -204,7 +204,7 @@ function M({ reportId: D, runId: M }) {
204
204
  ]
205
205
  })
206
206
  ]
207
- }), !n && /* @__PURE__ */ m(t, {
207
+ }), !t && /* @__PURE__ */ m(n, {
208
208
  label: "Revoke",
209
209
  icon: null,
210
210
  title: "Revoke this share?",
@@ -219,7 +219,7 @@ function M({ reportId: D, runId: M }) {
219
219
  })
220
220
  })]
221
221
  }),
222
- /* @__PURE__ */ m(S, { children: /* @__PURE__ */ m(_, {
222
+ /* @__PURE__ */ m(C, { children: /* @__PURE__ */ m(_, {
223
223
  variant: "outline",
224
224
  onClick: () => P(!1),
225
225
  children: "Close"
@@ -1 +1 @@
1
- {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../src/planmagnet/reports/ShareDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n Input,\n Label,\n} from '@burdenoff/fe-libs/ui';\nimport { Check, Copy, Share2 } from 'lucide-react';\nimport { copyTextToClipboard } from '@burdenoff/fe-libs/sharing';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport { throwOnError } from '../utils/mutationResult';\nimport { CREATE_REPORT_SHARE, LIST_REPORT_SHARES, REVOKE_REPORT_SHARE } from './reportGraphql';\nimport { sharedReportLink } from './sharedReportRoute';\nimport type { ReportShare } from './reportModel';\n\n/** Share links (ANR-16), owner-only — a report's owner creates a link to one persisted run; anyone with the\n * link (and the password, if set) can view that run's snapshot read-only at `/public/report/<token>`, no\n * workspace login required. The raw token is shown exactly once, right after creation. */\n\nconst DAY_MS = 86_400_000;\nconst DEFAULT_EXPIRY_DAYS = 7;\nconst MAX_EXPIRY_DAYS = 90;\n\nfunction dateInputValue(daysFromNow: number): string {\n return new Date(Date.now() + daysFromNow * DAY_MS).toISOString().slice(0, 10);\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nexport function ShareDialog({ reportId, runId }: { reportId: string; runId: string | null }) {\n const [open, setOpen] = useState(false);\n const [expiresAt, setExpiresAt] = useState(() => dateInputValue(DEFAULT_EXPIRY_DAYS));\n const [password, setPassword] = useState('');\n const [watermark, setWatermark] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [justCreated, setJustCreated] = useState<ReportShare | null>(null);\n const [copied, setCopied] = useState(false);\n\n const shares = useQuery<{ planmagnetReportShares: ReportShare[] }>(LIST_REPORT_SHARES, {\n variables: { reportId },\n skip: !open,\n });\n const [createShare, createState] = useMutation(CREATE_REPORT_SHARE, {\n refetchQueries: [{ query: LIST_REPORT_SHARES, variables: { reportId } }],\n });\n const [revokeShare] = useMutation(REVOKE_REPORT_SHARE, {\n refetchQueries: [{ query: LIST_REPORT_SHARES, variables: { reportId } }],\n });\n\n const handleCreate = async () => {\n if (!runId) return;\n setError(null);\n setJustCreated(null);\n try {\n const result = await throwOnError(\n createShare({\n variables: {\n runId,\n input: {\n expiresAt: `${expiresAt}T23:59:59.999Z`,\n password: password.trim() || undefined,\n watermark: watermark.trim() || undefined,\n },\n },\n })\n );\n const share = (result.data as { planmagnetCreateReportShare?: ReportShare } | undefined)\n ?.planmagnetCreateReportShare;\n if (share) {\n setJustCreated(share);\n setCopied(false);\n setPassword('');\n setWatermark('');\n }\n } catch (e) {\n setError(mutationErrorMessage(e, 'The share link could not be created.'));\n }\n };\n\n const handleRevoke = async (id: string) => {\n setError(null);\n try {\n await throwOnError(revokeShare({ variables: { id } }));\n if (justCreated?.id === id) setJustCreated(null);\n } catch (e) {\n setError(mutationErrorMessage(e, 'The share could not be revoked.'));\n }\n };\n\n const copyLink = async (token: string) => {\n try {\n await copyTextToClipboard(sharedReportLink(token));\n setCopied(true);\n } catch {\n setError('Could not copy — copy the link manually.');\n }\n };\n\n const list = shares.data?.planmagnetReportShares ?? [];\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) {\n setJustCreated(null);\n setError(null);\n }\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"report-share\">\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Share this report</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Anyone with the link can view the most recently shared run, read-only, without signing\n in.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {!runId && (\n <p className=\"text-sm text-muted-foreground\">\n Run this report at least once before sharing it — a share links to one specific run.\n </p>\n )}\n\n {runId && (\n <div className=\"space-y-3 rounded-md border border-border p-3\">\n <div>\n <Label htmlFor=\"share-expiry\">Expires</Label>\n <Input\n id=\"share-expiry\"\n type=\"date\"\n className=\"mt-1\"\n value={expiresAt}\n min={dateInputValue(1)}\n max={dateInputValue(MAX_EXPIRY_DAYS)}\n onChange={(e) => setExpiresAt(e.target.value)}\n />\n </div>\n <div>\n <Label htmlFor=\"share-password\">Password (optional)</Label>\n <Input\n id=\"share-password\"\n type=\"password\"\n className=\"mt-1\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Leave blank for no password\"\n />\n </div>\n <div>\n <Label htmlFor=\"share-watermark\">Watermark text (optional)</Label>\n <Input\n id=\"share-watermark\"\n className=\"mt-1\"\n value={watermark}\n onChange={(e) => setWatermark(e.target.value)}\n placeholder={`Shared via PlanMagnet on ${new Date().toLocaleDateString()}`}\n />\n </div>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={createState.loading}\n data-testid=\"report-share-create\"\n >\n {createState.loading ? 'Creating…' : 'Create share link'}\n </Button>\n </div>\n )}\n\n {justCreated?.token && (\n <div\n className=\"space-y-2 rounded-md border border-primary bg-primary/5 p-3\"\n data-testid=\"report-share-link\"\n >\n <p className=\"text-sm font-medium\">Link created — copy it now.</p>\n <p className=\"text-xs text-muted-foreground\">\n You will not see this link again. If you lose it, revoke this share and create a new\n one.\n </p>\n <div className=\"flex items-center gap-2\">\n <Input readOnly value={sharedReportLink(justCreated.token)} className=\"text-xs\" />\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => copyLink(justCreated.token as string)}\n >\n {copied ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copied ? 'Copied' : 'Copy'}\n </Button>\n </div>\n </div>\n )}\n\n {error && <ErrorLine message={error} />}\n\n <div className=\"space-y-2\" data-testid=\"report-shares\">\n <h3 className=\"text-sm font-semibold\">Existing shares</h3>\n {shares.loading && !shares.data ? (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n ) : list.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No shares yet.</p>\n ) : (\n <ul className=\"space-y-2\">\n {list.map((share) => {\n const revoked = !!share.revokedAt;\n const expired =\n !revoked && new Date(share.expiresAt).getTime() < new Date().getTime();\n return (\n <li\n key={share.id}\n className=\"flex flex-wrap items-center justify-between gap-2 rounded-md border border-border p-2 text-xs\"\n >\n <div className=\"space-y-0.5\">\n <div className=\"flex items-center gap-1.5\">\n <Badge variant={revoked ? 'secondary' : expired ? 'warning' : 'success'}>\n {revoked ? 'Revoked' : expired ? 'Expired' : 'Active'}\n </Badge>\n {share.hasPassword && <Badge variant=\"outline\">Password</Badge>}\n </div>\n <p className=\"text-muted-foreground\">\n Created {formatDate(share.createdAt)} · Expires{' '}\n {formatDate(share.expiresAt)}\n </p>\n <p className=\"text-muted-foreground\">\n Accessed {share.accessCount} time{share.accessCount === 1 ? '' : 's'}\n {share.lastAccessedAt ? ` · last ${formatDate(share.lastAccessedAt)}` : ''}\n </p>\n </div>\n {!revoked && (\n <ConfirmButton\n label=\"Revoke\"\n icon={null}\n title=\"Revoke this share?\"\n description=\"Anyone with this link loses access immediately. This cannot be undone.\"\n confirmLabel=\"Revoke\"\n destructive\n variant=\"outline\"\n disabled={false}\n onConfirm={() => void handleRevoke(share.id)}\n />\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Close\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ShareDialog.js","names":[],"sources":["../../../src/planmagnet/reports/ShareDialog.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport {\n Badge,\n Button,\n ResponsiveDialog,\n ResponsiveDialogContent,\n ResponsiveDialogDescription,\n ResponsiveDialogFooter,\n ResponsiveDialogHeader,\n ResponsiveDialogTitle,\n ResponsiveDialogTrigger,\n Input,\n Label,\n} from '@burdenoff/fe-libs/ui';\nimport { Check, Copy, Share2 } from 'lucide-react';\nimport { copyTextToClipboard } from '@burdenoff/fe-libs/sharing';\nimport { ConfirmButton, ErrorLine } from '../controls/ConfirmButton';\nimport { mutationErrorMessage } from '../utils/graphqlErrors';\nimport { throwOnError } from '../utils/mutationResult';\nimport { CREATE_REPORT_SHARE, LIST_REPORT_SHARES, REVOKE_REPORT_SHARE } from './reportGraphql';\nimport { sharedReportLink } from './sharedReportRoute';\nimport type { ReportShare } from './reportModel';\n\n/** Share links (ANR-16), owner-only — a report's owner creates a link to one persisted run; anyone with the\n * link (and the password, if set) can view that run's snapshot read-only at `/public/report/<token>`, no\n * workspace login required. The raw token is shown exactly once, right after creation. */\n\nconst DAY_MS = 86_400_000;\nconst DEFAULT_EXPIRY_DAYS = 7;\nconst MAX_EXPIRY_DAYS = 90;\n\nfunction dateInputValue(daysFromNow: number): string {\n return new Date(Date.now() + daysFromNow * DAY_MS).toISOString().slice(0, 10);\n}\n\nfunction formatDate(iso: string): string {\n return new Date(iso).toLocaleString();\n}\n\nexport function ShareDialog({ reportId, runId }: { reportId: string; runId: string | null }) {\n const [open, setOpen] = useState(false);\n const [expiresAt, setExpiresAt] = useState(() => dateInputValue(DEFAULT_EXPIRY_DAYS));\n const [password, setPassword] = useState('');\n const [watermark, setWatermark] = useState('');\n const [error, setError] = useState<string | null>(null);\n const [justCreated, setJustCreated] = useState<ReportShare | null>(null);\n const [copied, setCopied] = useState(false);\n\n const shares = useQuery<{ planmagnetReportShares: ReportShare[] }>(LIST_REPORT_SHARES, {\n variables: { reportId },\n skip: !open,\n });\n const [createShare, createState] = useMutation(CREATE_REPORT_SHARE, {\n refetchQueries: [{ query: LIST_REPORT_SHARES, variables: { reportId } }],\n });\n const [revokeShare] = useMutation(REVOKE_REPORT_SHARE, {\n refetchQueries: [{ query: LIST_REPORT_SHARES, variables: { reportId } }],\n });\n\n const handleCreate = async () => {\n if (!runId) return;\n setError(null);\n setJustCreated(null);\n try {\n const result = await throwOnError(\n createShare({\n variables: {\n runId,\n input: {\n expiresAt: `${expiresAt}T23:59:59.999Z`,\n password: password.trim() || undefined,\n watermark: watermark.trim() || undefined,\n },\n },\n })\n );\n const share = (result.data as { planmagnetCreateReportShare?: ReportShare } | undefined)\n ?.planmagnetCreateReportShare;\n if (share) {\n setJustCreated(share);\n setCopied(false);\n setPassword('');\n setWatermark('');\n }\n } catch (e) {\n setError(mutationErrorMessage(e, 'The share link could not be created.'));\n }\n };\n\n const handleRevoke = async (id: string) => {\n setError(null);\n try {\n await throwOnError(revokeShare({ variables: { id } }));\n if (justCreated?.id === id) setJustCreated(null);\n } catch (e) {\n setError(mutationErrorMessage(e, 'The share could not be revoked.'));\n }\n };\n\n const copyLink = async (token: string) => {\n try {\n await copyTextToClipboard(sharedReportLink(token));\n setCopied(true);\n } catch {\n setError('Could not copy — copy the link manually.');\n }\n };\n\n const list = shares.data?.planmagnetReportShares ?? [];\n\n return (\n <ResponsiveDialog\n open={open}\n onOpenChange={(next) => {\n setOpen(next);\n if (!next) {\n setJustCreated(null);\n setError(null);\n }\n }}\n >\n <ResponsiveDialogTrigger asChild>\n <Button size=\"sm\" variant=\"outline\" data-testid=\"report-share\">\n <Share2 className=\"mr-1 h-4 w-4\" />\n Share\n </Button>\n </ResponsiveDialogTrigger>\n <ResponsiveDialogContent className=\"max-h-[85vh] overflow-y-auto sm:max-w-lg\">\n <ResponsiveDialogHeader>\n <ResponsiveDialogTitle>Share this report</ResponsiveDialogTitle>\n <ResponsiveDialogDescription>\n Anyone with the link can view the most recently shared run, read-only, without signing\n in.\n </ResponsiveDialogDescription>\n </ResponsiveDialogHeader>\n\n {!runId && (\n <p className=\"text-sm text-muted-foreground\">\n Run this report at least once before sharing it — a share links to one specific run.\n </p>\n )}\n\n {runId && (\n <div className=\"space-y-3 rounded-md border border-border p-3\">\n <div>\n <Label htmlFor=\"share-expiry\">Expires</Label>\n <Input\n id=\"share-expiry\"\n type=\"date\"\n className=\"mt-1\"\n value={expiresAt}\n min={dateInputValue(1)}\n max={dateInputValue(MAX_EXPIRY_DAYS)}\n onChange={(e) => setExpiresAt(e.target.value)}\n />\n </div>\n <div>\n <Label htmlFor=\"share-password\">Password (optional)</Label>\n <Input\n id=\"share-password\"\n type=\"password\"\n className=\"mt-1\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"Leave blank for no password\"\n />\n </div>\n <div>\n <Label htmlFor=\"share-watermark\">Watermark text (optional)</Label>\n <Input\n id=\"share-watermark\"\n className=\"mt-1\"\n value={watermark}\n onChange={(e) => setWatermark(e.target.value)}\n placeholder={`Shared via PlanMagnet on ${new Date().toLocaleDateString()}`}\n />\n </div>\n <Button\n size=\"sm\"\n onClick={handleCreate}\n disabled={createState.loading}\n data-testid=\"report-share-create\"\n >\n {createState.loading ? 'Creating…' : 'Create share link'}\n </Button>\n </div>\n )}\n\n {justCreated?.token && (\n <div\n className=\"space-y-2 rounded-md border border-primary bg-primary/5 p-3\"\n data-testid=\"report-share-link\"\n >\n <p className=\"text-sm font-medium\">Link created — copy it now.</p>\n <p className=\"text-xs text-muted-foreground\">\n You will not see this link again. If you lose it, revoke this share and create a new\n one.\n </p>\n <div className=\"flex items-center gap-2\">\n <Input readOnly value={sharedReportLink(justCreated.token)} className=\"text-xs\" />\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() => copyLink(justCreated.token as string)}\n >\n {copied ? <Check className=\"h-4 w-4\" /> : <Copy className=\"h-4 w-4\" />}\n {copied ? 'Copied' : 'Copy'}\n </Button>\n </div>\n </div>\n )}\n\n {error && <ErrorLine message={error} />}\n\n <div className=\"space-y-2\" data-testid=\"report-shares\">\n <h3 className=\"text-sm font-semibold\">Existing shares</h3>\n {shares.loading && !shares.data ? (\n <p className=\"text-sm text-muted-foreground\">Loading…</p>\n ) : list.length === 0 ? (\n <p className=\"text-sm text-muted-foreground\">No shares yet.</p>\n ) : (\n <ul className=\"space-y-2\">\n {list.map((share) => {\n const revoked = !!share.revokedAt;\n const expired =\n !revoked && new Date(share.expiresAt).getTime() < new Date().getTime();\n return (\n <li\n key={share.id}\n className=\"flex flex-wrap items-center justify-between gap-2 rounded-md border border-border p-2 text-xs\"\n >\n <div className=\"space-y-0.5\">\n <div className=\"flex items-center gap-1.5\">\n <Badge variant={revoked ? 'secondary' : expired ? 'warning' : 'success'}>\n {revoked ? 'Revoked' : expired ? 'Expired' : 'Active'}\n </Badge>\n {share.hasPassword && <Badge variant=\"outline\">Password</Badge>}\n </div>\n <p className=\"text-muted-foreground\">\n Created {formatDate(share.createdAt)} · Expires{' '}\n {formatDate(share.expiresAt)}\n </p>\n <p className=\"text-muted-foreground\">\n Accessed {share.accessCount} time{share.accessCount === 1 ? '' : 's'}\n {share.lastAccessedAt ? ` · last ${formatDate(share.lastAccessedAt)}` : ''}\n </p>\n </div>\n {!revoked && (\n <ConfirmButton\n label=\"Revoke\"\n icon={null}\n title=\"Revoke this share?\"\n description=\"Anyone with this link loses access immediately. This cannot be undone.\"\n confirmLabel=\"Revoke\"\n destructive\n variant=\"outline\"\n disabled={false}\n onConfirm={() => void handleRevoke(share.id)}\n />\n )}\n </li>\n );\n })}\n </ul>\n )}\n </div>\n\n <ResponsiveDialogFooter>\n <Button variant=\"outline\" onClick={() => setOpen(false)}>\n Close\n </Button>\n </ResponsiveDialogFooter>\n </ResponsiveDialogContent>\n </ResponsiveDialog>\n );\n}\n"],"mappings":"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"}
@@ -3,8 +3,9 @@
3
3
  * (`product/routes.ts`, `integrations/routes.ts`), this one is NOT relative to this MFE's `basePath` — the host
4
4
  * shell mounts `SharedReportPage` on its own top-level router, OUTSIDE the authenticated app shell/basePath
5
5
  * tree entirely (no nav chrome, no auth guard), so the path is absolute and fixed. It lives under `/public/`
6
- * because fe-libs' shell-route table already renders every `/public/*` path shell-less, so an anonymous reader
7
- * gets the bare page with no signed-in chrome and no host needs its own exception.
6
+ * because fe-libs' shell-route table gives every `/public/*` path the public layout — the product header and
7
+ * footer, but no sidebar or breadcrumb — so an anonymous reader never sees the signed-in navigation and no host
8
+ * needs its own exception.
8
9
  */
9
10
  export declare const SHARED_REPORT_BASE_PATH = "/public/report";
10
11
  /** For the host's own `<Route path={SHARED_REPORT_ROUTE_PATH} element={<SharedReportPage />} />`. */
@@ -1 +1 @@
1
- {"version":3,"file":"sharedReportRoute.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/reports/sharedReportRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,eAAO,MAAM,uBAAuB,mBAAmB,CAAC;AACxD,qGAAqG;AACrG,eAAO,MAAM,wBAAwB,0BAAsC,CAAC;AAE5E,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD"}
1
+ {"version":3,"file":"sharedReportRoute.d.ts","sourceRoot":"","sources":["../../../src/planmagnet/reports/sharedReportRoute.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,mBAAmB,CAAC;AACxD,qGAAqG;AACrG,eAAO,MAAM,wBAAwB,0BAAsC,CAAC;AAE5E,0EAA0E;AAC1E,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD"}
@@ -1 +1 @@
1
- {"version":3,"file":"sharedReportRoute.js","names":[],"sources":["../../../src/planmagnet/reports/sharedReportRoute.ts"],"sourcesContent":["/**\n * The public shared-report route (ANR-17). Unlike every other route constant in this package\n * (`product/routes.ts`, `integrations/routes.ts`), this one is NOT relative to this MFE's `basePath` — the host\n * shell mounts `SharedReportPage` on its own top-level router, OUTSIDE the authenticated app shell/basePath\n * tree entirely (no nav chrome, no auth guard), so the path is absolute and fixed. It lives under `/public/`\n * because fe-libs' shell-route table already renders every `/public/*` path shell-less, so an anonymous reader\n * gets the bare page with no signed-in chrome and no host needs its own exception.\n */\nexport const SHARED_REPORT_BASE_PATH = '/public/report';\n/** For the host's own `<Route path={SHARED_REPORT_ROUTE_PATH} element={<SharedReportPage />} />`. */\nexport const SHARED_REPORT_ROUTE_PATH = `${SHARED_REPORT_BASE_PATH}/:token`;\n\n/** The full, copyable link for a share token, from the current origin. */\nexport function sharedReportLink(token: string): string {\n const origin = typeof window !== 'undefined' ? window.location.origin : '';\n return `${origin}${SHARED_REPORT_BASE_PATH}/${encodeURIComponent(token)}`;\n}\n"],"mappings":";AAQA,IAAa,IAA0B,kBAE1B,IAA2B,GAAG,EAAwB;AAGnE,SAAgB,EAAiB,GAAuB;AAEtD,QAAO,GADQ,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS,KACrD,EAAwB,GAAG,mBAAmB,EAAM"}
1
+ {"version":3,"file":"sharedReportRoute.js","names":[],"sources":["../../../src/planmagnet/reports/sharedReportRoute.ts"],"sourcesContent":["/**\n * The public shared-report route (ANR-17). Unlike every other route constant in this package\n * (`product/routes.ts`, `integrations/routes.ts`), this one is NOT relative to this MFE's `basePath` — the host\n * shell mounts `SharedReportPage` on its own top-level router, OUTSIDE the authenticated app shell/basePath\n * tree entirely (no nav chrome, no auth guard), so the path is absolute and fixed. It lives under `/public/`\n * because fe-libs' shell-route table gives every `/public/*` path the public layout — the product header and\n * footer, but no sidebar or breadcrumb — so an anonymous reader never sees the signed-in navigation and no host\n * needs its own exception.\n */\nexport const SHARED_REPORT_BASE_PATH = '/public/report';\n/** For the host's own `<Route path={SHARED_REPORT_ROUTE_PATH} element={<SharedReportPage />} />`. */\nexport const SHARED_REPORT_ROUTE_PATH = `${SHARED_REPORT_BASE_PATH}/:token`;\n\n/** The full, copyable link for a share token, from the current origin. */\nexport function sharedReportLink(token: string): string {\n const origin = typeof window !== 'undefined' ? window.location.origin : '';\n return `${origin}${SHARED_REPORT_BASE_PATH}/${encodeURIComponent(token)}`;\n}\n"],"mappings":";AASA,IAAa,IAA0B,kBAE1B,IAA2B,GAAG,EAAwB;AAGnE,SAAgB,EAAiB,GAAuB;AAEtD,QAAO,GADQ,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS,KACrD,EAAwB,GAAG,mBAAmB,EAAM"}