@burdenoff/planmagnet-fe-sdk 2026.706.3 → 2026.706.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -825,6 +825,13 @@ interface TaskDetailCoreProps {
825
825
  commentsEntityType?: string;
826
826
  /** The current actor — enables "My tasks", comment authorship + own-comment delete. */
827
827
  currentActorId?: string;
828
+ /** Fired when a posted comment @-mentions members (host notifies them). */
829
+ onMention?: (payload: {
830
+ mentionedIds: string[];
831
+ text: string;
832
+ entityId: string;
833
+ commentId: string;
834
+ }) => void;
828
835
  /** Extra classes on the root container. */
829
836
  className?: string;
830
837
  }
@@ -833,7 +840,7 @@ interface TaskDetailCoreProps {
833
840
  * edit. Fetches via `planmagnetWorkItem(id)`, saves via
834
841
  * `planmagnetUpdateWorkItem`. BYO-Apollo, semantic-token only, farm-free.
835
842
  */
836
- declare function TaskDetailCore({ id, readonly, hiddenFields, hideHeader, onUpdated, onDeleted, onError, resolveAssignee, assigneeOptions, linkSourceProduct, renderLinkIcon, onOpenItem, onAddSubtask, commentsEntityType, currentActorId, className, }: TaskDetailCoreProps): react.JSX.Element;
843
+ declare function TaskDetailCore({ id, readonly, hiddenFields, hideHeader, onUpdated, onDeleted, onError, resolveAssignee, assigneeOptions, linkSourceProduct, renderLinkIcon, onOpenItem, onAddSubtask, commentsEntityType, currentActorId, onMention, className, }: TaskDetailCoreProps): react.JSX.Element;
837
844
 
838
845
  interface TaskDetailProps extends TaskDetailCoreProps {
839
846
  /** Workspace API gateway URL (URL mode). */
@@ -885,6 +892,18 @@ interface TaskCommentsCoreProps {
885
892
  resolveAssignee?: ResolveAssignee;
886
893
  /** Member directory for @-mention autocomplete. Omit to disable mentions. */
887
894
  members?: AssigneeOption[];
895
+ /**
896
+ * Fired after a comment posts that @-mentions members — the host can notify
897
+ * them (e.g. via global-notification-svc). `mentionedIds` are workspace member
898
+ * ids (the SDK does not send them to the comment service; see the note in
899
+ * `post`). Kept generic so the SDK stays farm-free.
900
+ */
901
+ onMention?: (payload: {
902
+ mentionedIds: string[];
903
+ text: string;
904
+ entityId: string;
905
+ commentId: string;
906
+ }) => void;
888
907
  /** Hide the composer + delete affordances (pure read-only). */
889
908
  readonly?: boolean;
890
909
  /** Extra classes on the root container. */
@@ -899,7 +918,7 @@ interface TaskCommentsCoreProps {
899
918
  * a workspace Member gets editor on `entity-comment`); the composer fails open
900
919
  * and surfaces a graceful error if the server denies it.
901
920
  */
902
- declare function TaskCommentsCore({ entityType, entityId, currentActorId, resolveAssignee, members, readonly, className, }: TaskCommentsCoreProps): react.JSX.Element;
921
+ declare function TaskCommentsCore({ entityType, entityId, currentActorId, resolveAssignee, members, onMention, readonly, className, }: TaskCommentsCoreProps): react.JSX.Element;
903
922
 
904
923
  /**
905
924
  * TaskLabels — every user-facing string the SDK renders, so a host (e.g.
@@ -1079,6 +1098,13 @@ interface TasksPageCoreProps {
1079
1098
  * (e.g. "planmagnet_workitem"). Omit to hide comments.
1080
1099
  */
1081
1100
  commentsEntityType?: string;
1101
+ /** Fired when a posted comment @-mentions members (host notifies them). */
1102
+ onMention?: (payload: {
1103
+ mentionedIds: string[];
1104
+ text: string;
1105
+ entityId: string;
1106
+ commentId: string;
1107
+ }) => void;
1082
1108
  /** Host-provided translations for every UI string (English defaults otherwise). */
1083
1109
  labels?: PartialTaskLabels;
1084
1110
  /**
@@ -1111,7 +1137,7 @@ interface TasksPageCoreProps {
1111
1137
  * BYO-Apollo: reads the ambient `<ApolloProvider>` and takes `workspaceId` from
1112
1138
  * a prop or the PlanMagnetProvider context. Semantic tokens only, farm-free.
1113
1139
  */
1114
- declare function TasksPageCore({ workspaceId: workspaceIdProp, projectId, boardProjectId, columns, readonly, hiddenFields, views, defaultView, confirmMove, resolveAssignee, linkSourceProduct, renderLinkIcon, assigneeOptions, currentActorId, commentsEntityType, labels, openItemId, header, onTaskSaved, createSurface, detailSurface, className, }: TasksPageCoreProps): react.JSX.Element;
1140
+ declare function TasksPageCore({ workspaceId: workspaceIdProp, projectId, boardProjectId, columns, readonly, hiddenFields, views, defaultView, confirmMove, resolveAssignee, linkSourceProduct, renderLinkIcon, assigneeOptions, currentActorId, commentsEntityType, onMention, labels, openItemId, header, onTaskSaved, createSurface, detailSurface, className, }: TasksPageCoreProps): react.JSX.Element;
1115
1141
 
1116
1142
  interface TasksPageProps extends TasksPageCoreProps {
1117
1143
  /** Workspace API gateway URL (URL mode). */
package/dist/index.js CHANGED
@@ -565,6 +565,15 @@ function parseDate(iso) {
565
565
  const d = parseISO(iso);
566
566
  return isValid(d) ? d : null;
567
567
  }
568
+ function parseDueDate(iso) {
569
+ if (!iso) return null;
570
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
571
+ if (m) {
572
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
573
+ return isValid(d) ? d : null;
574
+ }
575
+ return parseDate(iso);
576
+ }
568
577
  function toIsoDate(value) {
569
578
  if (!value) return void 0;
570
579
  const iso = /^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00.000Z` : value;
@@ -572,7 +581,7 @@ function toIsoDate(value) {
572
581
  return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
573
582
  }
574
583
  function formatDueDate(iso) {
575
- const d = parseDate(iso);
584
+ const d = parseDueDate(iso);
576
585
  if (!d) return null;
577
586
  const now = /* @__PURE__ */ new Date();
578
587
  return format(
@@ -580,12 +589,16 @@ function formatDueDate(iso) {
580
589
  d.getFullYear() === now.getFullYear() ? "MMM d" : "MMM d yyyy"
581
590
  );
582
591
  }
592
+ function formatDueDateFull(iso) {
593
+ const d = parseDueDate(iso);
594
+ return d ? format(d, "MMM d, yyyy") : null;
595
+ }
583
596
  function formatDateFull(iso) {
584
597
  const d = parseDate(iso);
585
598
  return d ? format(d, "MMM d, yyyy") : null;
586
599
  }
587
600
  function isOverdue(iso, status) {
588
- const d = parseDate(iso);
601
+ const d = parseDueDate(iso);
589
602
  if (!d) return false;
590
603
  if (status && CLOSED_STATUSES.has(status)) return false;
591
604
  return startOfDay(d).getTime() < startOfDay(/* @__PURE__ */ new Date()).getTime();
@@ -2091,7 +2104,7 @@ function TaskTimelineCore({
2091
2104
  const od = [];
2092
2105
  const ud = [];
2093
2106
  for (const t of tasks) {
2094
- const d = parseDate(t.dueDate);
2107
+ const d = parseDueDate(t.dueDate);
2095
2108
  if (!d) {
2096
2109
  ud.push(t);
2097
2110
  continue;
@@ -2622,6 +2635,7 @@ function TaskCommentsCore({
2622
2635
  currentActorId,
2623
2636
  resolveAssignee,
2624
2637
  members,
2638
+ onMention,
2625
2639
  readonly,
2626
2640
  className
2627
2641
  }) {
@@ -2673,35 +2687,33 @@ function TaskCommentsCore({
2673
2687
  const authorName = (id) => id === currentActorId ? L.you : resolveAssignee?.(id)?.name ?? void 0;
2674
2688
  const post = async ({ text, mentionUserIds }, parentCommentId) => {
2675
2689
  setError(null);
2676
- const base = {
2677
- entityType,
2678
- entityId,
2679
- content: { text },
2680
- contentType: "TEXT",
2681
- ...parentCommentId ? { parentCommentId } : {}
2682
- };
2683
- const create = (withMentions) => createComment({
2684
- variables: {
2685
- input: withMentions && mentionUserIds.length ? { ...base, mentionUserIds } : base
2686
- }
2687
- });
2688
2690
  try {
2689
- await create(true);
2690
- } catch (err) {
2691
- if (mentionUserIds.length) {
2692
- try {
2693
- await create(false);
2694
- } catch (err2) {
2695
- setError(L.failedToComment);
2696
- throw err2;
2691
+ const res = await createComment({
2692
+ variables: {
2693
+ input: {
2694
+ entityType,
2695
+ entityId,
2696
+ content: { text },
2697
+ contentType: "TEXT",
2698
+ ...parentCommentId ? { parentCommentId } : {}
2699
+ }
2697
2700
  }
2698
- } else {
2699
- setError(L.failedToComment);
2700
- throw err;
2701
+ });
2702
+ await refetch();
2703
+ setReplyingTo(null);
2704
+ const toNotify = mentionUserIds.filter((id) => id !== currentActorId);
2705
+ if (toNotify.length && onMention) {
2706
+ onMention({
2707
+ mentionedIds: toNotify,
2708
+ text,
2709
+ entityId,
2710
+ commentId: res.data?.createEntityComment?.id ?? ""
2711
+ });
2701
2712
  }
2713
+ } catch (err) {
2714
+ setError(L.failedToComment);
2715
+ throw err;
2702
2716
  }
2703
- await refetch();
2704
- setReplyingTo(null);
2705
2717
  };
2706
2718
  const handleDelete = async (id) => {
2707
2719
  setError(null);
@@ -2821,6 +2833,7 @@ function TaskDetailCore({
2821
2833
  onAddSubtask,
2822
2834
  commentsEntityType,
2823
2835
  currentActorId,
2836
+ onMention,
2824
2837
  className
2825
2838
  }) {
2826
2839
  const client = useApolloClient();
@@ -3021,7 +3034,7 @@ function TaskDetailCore({
3021
3034
  ),
3022
3035
  /* @__PURE__ */ jsx("span", { className: "text-foreground", children: resolveAssignee?.(item.assigneeId)?.name ?? L.assigned })
3023
3036
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: L.unassigned }) }),
3024
- !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDateFull(item.dueDate) ?? "\u2014" }) }),
3037
+ !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDueDateFull(item.dueDate) ?? "\u2014" }) }),
3025
3038
  !hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: L.storyPointsLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
3026
3039
  !hidden.has("description") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3027
3040
  /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.description }),
@@ -3122,7 +3135,8 @@ function TaskDetailCore({
3122
3135
  entityId: item.id,
3123
3136
  currentActorId,
3124
3137
  resolveAssignee,
3125
- members: assigneeOptions
3138
+ members: assigneeOptions,
3139
+ onMention
3126
3140
  }
3127
3141
  ) })
3128
3142
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSave, className: "space-y-4", children: [
@@ -3338,6 +3352,7 @@ function TasksPageCore({
3338
3352
  assigneeOptions,
3339
3353
  currentActorId,
3340
3354
  commentsEntityType,
3355
+ onMention,
3341
3356
  labels,
3342
3357
  openItemId,
3343
3358
  header,
@@ -3441,7 +3456,8 @@ function TasksPageCore({
3441
3456
  onOpenItem: handleSelect,
3442
3457
  onAddSubtask: handleAddSubtask,
3443
3458
  commentsEntityType,
3444
- currentActorId
3459
+ currentActorId,
3460
+ onMention
3445
3461
  }
3446
3462
  ) : null;
3447
3463
  const listPriority = priority === "ALL" ? void 0 : priority;