@burdenoff/planmagnet-fe-sdk 2026.706.4 → 2026.706.6

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
@@ -740,6 +740,19 @@ interface TaskTimelineCoreProps {
740
740
  resolveAssignee?: ResolveAssignee;
741
741
  /** Fetch + flag project milestones on the timeline (default true). */
742
742
  showMilestones?: boolean;
743
+ /**
744
+ * Allow creating milestones from the timeline (an inline name + due-date
745
+ * form in the milestones strip). Requires a create project (see
746
+ * `milestoneProjectId`). Gate on the host's manage permission. Default false.
747
+ */
748
+ canCreateMilestone?: boolean;
749
+ /**
750
+ * Project new milestones are created under. Defaults to `projectId`. Set this
751
+ * when the timeline's task-filter project differs from the project milestones
752
+ * should belong to — e.g. an unscoped "all tasks" board that still has a
753
+ * default create-project.
754
+ */
755
+ milestoneProjectId?: string;
743
756
  className?: string;
744
757
  workspaceId?: string;
745
758
  }
@@ -752,7 +765,7 @@ interface TaskTimelineCoreProps {
752
765
  * honest to the data. Overdue and undated work is surfaced explicitly so nothing
753
766
  * hides. BYO-Apollo, semantic-token only, farm-free.
754
767
  */
755
- declare function TaskTimelineCore({ projectId, priority, type, search, assigneeId, defaultMode, onSelect, resolveAssignee, showMilestones, className, workspaceId: workspaceIdProp, }: TaskTimelineCoreProps): react.JSX.Element;
768
+ declare function TaskTimelineCore({ projectId, priority, type, search, assigneeId, defaultMode, onSelect, resolveAssignee, showMilestones, canCreateMilestone, milestoneProjectId, className, workspaceId: workspaceIdProp, }: TaskTimelineCoreProps): react.JSX.Element;
756
769
 
757
770
  /** The three task views the toolbar can switch between. */
758
771
  type TaskView = "board" | "list" | "timeline";
@@ -825,6 +838,13 @@ interface TaskDetailCoreProps {
825
838
  commentsEntityType?: string;
826
839
  /** The current actor — enables "My tasks", comment authorship + own-comment delete. */
827
840
  currentActorId?: string;
841
+ /** Fired when a posted comment @-mentions members (host notifies them). */
842
+ onMention?: (payload: {
843
+ mentionedIds: string[];
844
+ text: string;
845
+ entityId: string;
846
+ commentId: string;
847
+ }) => void;
828
848
  /** Extra classes on the root container. */
829
849
  className?: string;
830
850
  }
@@ -833,7 +853,7 @@ interface TaskDetailCoreProps {
833
853
  * edit. Fetches via `planmagnetWorkItem(id)`, saves via
834
854
  * `planmagnetUpdateWorkItem`. BYO-Apollo, semantic-token only, farm-free.
835
855
  */
836
- declare function TaskDetailCore({ id, readonly, hiddenFields, hideHeader, onUpdated, onDeleted, onError, resolveAssignee, assigneeOptions, linkSourceProduct, renderLinkIcon, onOpenItem, onAddSubtask, commentsEntityType, currentActorId, className, }: TaskDetailCoreProps): react.JSX.Element;
856
+ 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
857
 
838
858
  interface TaskDetailProps extends TaskDetailCoreProps {
839
859
  /** Workspace API gateway URL (URL mode). */
@@ -885,6 +905,18 @@ interface TaskCommentsCoreProps {
885
905
  resolveAssignee?: ResolveAssignee;
886
906
  /** Member directory for @-mention autocomplete. Omit to disable mentions. */
887
907
  members?: AssigneeOption[];
908
+ /**
909
+ * Fired after a comment posts that @-mentions members — the host can notify
910
+ * them (e.g. via global-notification-svc). `mentionedIds` are workspace member
911
+ * ids (the SDK does not send them to the comment service; see the note in
912
+ * `post`). Kept generic so the SDK stays farm-free.
913
+ */
914
+ onMention?: (payload: {
915
+ mentionedIds: string[];
916
+ text: string;
917
+ entityId: string;
918
+ commentId: string;
919
+ }) => void;
888
920
  /** Hide the composer + delete affordances (pure read-only). */
889
921
  readonly?: boolean;
890
922
  /** Extra classes on the root container. */
@@ -899,7 +931,7 @@ interface TaskCommentsCoreProps {
899
931
  * a workspace Member gets editor on `entity-comment`); the composer fails open
900
932
  * and surfaces a graceful error if the server denies it.
901
933
  */
902
- declare function TaskCommentsCore({ entityType, entityId, currentActorId, resolveAssignee, members, readonly, className, }: TaskCommentsCoreProps): react.JSX.Element;
934
+ declare function TaskCommentsCore({ entityType, entityId, currentActorId, resolveAssignee, members, onMention, readonly, className, }: TaskCommentsCoreProps): react.JSX.Element;
903
935
 
904
936
  /**
905
937
  * TaskLabels — every user-facing string the SDK renders, so a host (e.g.
@@ -927,6 +959,9 @@ interface TaskLabels {
927
959
  overdue: string;
928
960
  noDueDate: string;
929
961
  milestones: string;
962
+ addMilestone: string;
963
+ milestoneNamePlaceholder: string;
964
+ failedToCreateMilestone: string;
930
965
  today: string;
931
966
  week: string;
932
967
  month: string;
@@ -1042,6 +1077,11 @@ interface TasksPageCoreProps {
1042
1077
  columns?: BoardColumn[];
1043
1078
  /** Read-only — hides the "New task" button, disables drag + detail editing. */
1044
1079
  readonly?: boolean;
1080
+ /**
1081
+ * Allow creating milestones from the Timeline view (inline form in the
1082
+ * milestones strip). Requires a `boardProjectId`. Defaults to `!readonly`.
1083
+ */
1084
+ canCreateMilestone?: boolean;
1045
1085
  /**
1046
1086
  * Field names to hide across the board cards, create form, and detail view.
1047
1087
  * A product-agnostic hook so hosts can suppress fields that don't apply.
@@ -1079,6 +1119,13 @@ interface TasksPageCoreProps {
1079
1119
  * (e.g. "planmagnet_workitem"). Omit to hide comments.
1080
1120
  */
1081
1121
  commentsEntityType?: string;
1122
+ /** Fired when a posted comment @-mentions members (host notifies them). */
1123
+ onMention?: (payload: {
1124
+ mentionedIds: string[];
1125
+ text: string;
1126
+ entityId: string;
1127
+ commentId: string;
1128
+ }) => void;
1082
1129
  /** Host-provided translations for every UI string (English defaults otherwise). */
1083
1130
  labels?: PartialTaskLabels;
1084
1131
  /**
@@ -1111,7 +1158,7 @@ interface TasksPageCoreProps {
1111
1158
  * BYO-Apollo: reads the ambient `<ApolloProvider>` and takes `workspaceId` from
1112
1159
  * a prop or the PlanMagnetProvider context. Semantic tokens only, farm-free.
1113
1160
  */
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;
1161
+ declare function TasksPageCore({ workspaceId: workspaceIdProp, projectId, boardProjectId, columns, readonly, canCreateMilestone, hiddenFields, views, defaultView, confirmMove, resolveAssignee, linkSourceProduct, renderLinkIcon, assigneeOptions, currentActorId, commentsEntityType, onMention, labels, openItemId, header, onTaskSaved, createSurface, detailSurface, className, }: TasksPageCoreProps): react.JSX.Element;
1115
1162
 
1116
1163
  interface TasksPageProps extends TasksPageCoreProps {
1117
1164
  /** Workspace API gateway URL (URL mode). */
@@ -1229,11 +1276,28 @@ interface UseWorkItemLinksResult {
1229
1276
  */
1230
1277
  declare function useWorkItemLinks(workspaceId: string | undefined, sourceProduct: string | undefined, limit?: number): UseWorkItemLinksResult;
1231
1278
 
1279
+ /** Args for creating a milestone (subset of PlanmagnetCreateMilestoneInput). */
1280
+ interface CreateMilestoneArgs {
1281
+ projectId: string;
1282
+ name: string;
1283
+ /** A `yyyy-MM-dd` day; stored on the DateTime column at local noon. */
1284
+ dueDate?: string | null;
1285
+ status?: MilestoneStatus;
1286
+ description?: string | null;
1287
+ }
1232
1288
  interface UseMilestonesResult {
1233
1289
  milestones: Milestone[];
1234
1290
  /** Milestones grouped by their due day (yyyy-MM-dd), for timeline flags. */
1235
1291
  byDay: Map<string, Milestone[]>;
1236
1292
  loading: boolean;
1293
+ /** True while a create mutation is in flight. */
1294
+ creating: boolean;
1295
+ /**
1296
+ * Create a milestone and refetch the list. Returns the created milestone, or
1297
+ * throws so the caller can surface a toast. A `yyyy-MM-dd` dueDate is stored
1298
+ * at local noon so it lands on the intended day regardless of timezone.
1299
+ */
1300
+ createMilestone: (args: CreateMilestoneArgs) => Promise<Milestone | null>;
1237
1301
  }
1238
1302
  /**
1239
1303
  * useMilestones — fetch a project's milestones (e.g. a harvest window) for the
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { onError } from '@apollo/client/link/error';
6
6
  import { CombinedGraphQLErrors } from '@apollo/client/errors';
7
7
  import { jsx, jsxs } from 'react/jsx-runtime';
8
8
  import { gql } from '@apollo/client';
9
- import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, CalendarRange, List, LayoutGrid, Search, Plus, Trash2 } from 'lucide-react';
9
+ import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, Plus, CalendarRange, List, LayoutGrid, Search, Trash2 } from 'lucide-react';
10
10
  import { clsx } from 'clsx';
11
11
  import { twMerge } from 'tailwind-merge';
12
12
  import { addWeeks, startOfWeek, endOfWeek, eachDayOfInterval, addMonths, startOfMonth, endOfMonth, format, isToday, isSameMonth, startOfDay, parseISO, isValid } from 'date-fns';
@@ -507,6 +507,14 @@ var LIST_MILESTONES = gql`
507
507
  }
508
508
  ${MILESTONE_FIELDS}
509
509
  `;
510
+ var CREATE_MILESTONE = gql`
511
+ mutation CreateMilestone($input: PlanmagnetCreateMilestoneInput!) {
512
+ planmagnetCreateMilestone(input: $input) {
513
+ ...MilestoneFields
514
+ }
515
+ }
516
+ ${MILESTONE_FIELDS}
517
+ `;
510
518
  var DELETE_WORK_ITEM = gql`
511
519
  mutation DeleteWorkItem($id: ID!) {
512
520
  planmagnetDeleteWorkItem(id: $id)
@@ -565,6 +573,15 @@ function parseDate(iso) {
565
573
  const d = parseISO(iso);
566
574
  return isValid(d) ? d : null;
567
575
  }
576
+ function parseDueDate(iso) {
577
+ if (!iso) return null;
578
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(iso);
579
+ if (m) {
580
+ const d = new Date(Number(m[1]), Number(m[2]) - 1, Number(m[3]));
581
+ return isValid(d) ? d : null;
582
+ }
583
+ return parseDate(iso);
584
+ }
568
585
  function toIsoDate(value) {
569
586
  if (!value) return void 0;
570
587
  const iso = /^\d{4}-\d{2}-\d{2}$/.test(value) ? `${value}T00:00:00.000Z` : value;
@@ -572,7 +589,7 @@ function toIsoDate(value) {
572
589
  return Number.isNaN(d.getTime()) ? void 0 : d.toISOString();
573
590
  }
574
591
  function formatDueDate(iso) {
575
- const d = parseDate(iso);
592
+ const d = parseDueDate(iso);
576
593
  if (!d) return null;
577
594
  const now = /* @__PURE__ */ new Date();
578
595
  return format(
@@ -580,12 +597,16 @@ function formatDueDate(iso) {
580
597
  d.getFullYear() === now.getFullYear() ? "MMM d" : "MMM d yyyy"
581
598
  );
582
599
  }
600
+ function formatDueDateFull(iso) {
601
+ const d = parseDueDate(iso);
602
+ return d ? format(d, "MMM d, yyyy") : null;
603
+ }
583
604
  function formatDateFull(iso) {
584
605
  const d = parseDate(iso);
585
606
  return d ? format(d, "MMM d, yyyy") : null;
586
607
  }
587
608
  function isOverdue(iso, status) {
588
- const d = parseDate(iso);
609
+ const d = parseDueDate(iso);
589
610
  if (!d) return false;
590
611
  if (status && CLOSED_STATUSES.has(status)) return false;
591
612
  return startOfDay(d).getTime() < startOfDay(/* @__PURE__ */ new Date()).getTime();
@@ -759,6 +780,9 @@ var DEFAULT_TASK_LABELS = {
759
780
  overdue: "{n} overdue",
760
781
  noDueDate: "No due date ({n})",
761
782
  milestones: "Milestones",
783
+ addMilestone: "Add milestone",
784
+ milestoneNamePlaceholder: "Milestone name",
785
+ failedToCreateMilestone: "Failed to create milestone",
762
786
  today: "Today",
763
787
  week: "Week",
764
788
  month: "Month",
@@ -1983,17 +2007,52 @@ function TaskList({
1983
2007
  }
1984
2008
  );
1985
2009
  }
2010
+ function dayToLocalNoonIso(day) {
2011
+ const m = /^(\d{4})-(\d{2})-(\d{2})/.exec(day);
2012
+ if (!m) return void 0;
2013
+ return new Date(
2014
+ Number(m[1]),
2015
+ Number(m[2]) - 1,
2016
+ Number(m[3]),
2017
+ 12,
2018
+ 0,
2019
+ 0
2020
+ ).toISOString();
2021
+ }
1986
2022
  function useMilestones(workspaceId, opts = {}) {
1987
2023
  const { projectId, enabled = true, limit = 100 } = opts;
1988
- const { data, loading } = useQuery(LIST_MILESTONES, {
1989
- variables: {
1990
- workspaceId,
1991
- ...projectId ? { filter: { projectId } } : {},
1992
- pagination: { first: limit }
2024
+ const { data, loading, refetch } = useQuery(
2025
+ LIST_MILESTONES,
2026
+ {
2027
+ variables: {
2028
+ workspaceId,
2029
+ ...projectId ? { filter: { projectId } } : {},
2030
+ pagination: { first: limit }
2031
+ },
2032
+ skip: !workspaceId || !enabled,
2033
+ errorPolicy: "all"
2034
+ }
2035
+ );
2036
+ const [createMutation, { loading: creating }] = useMutation(CREATE_MILESTONE);
2037
+ const createMilestone = useCallback(
2038
+ async (args) => {
2039
+ const iso = args.dueDate ? dayToLocalNoonIso(args.dueDate) : void 0;
2040
+ const res = await createMutation({
2041
+ variables: {
2042
+ input: {
2043
+ projectId: args.projectId,
2044
+ name: args.name,
2045
+ ...iso ? { dueDate: iso } : {},
2046
+ ...args.status ? { status: args.status } : {},
2047
+ ...args.description ? { description: args.description } : {}
2048
+ }
2049
+ }
2050
+ });
2051
+ await refetch();
2052
+ return res.data?.planmagnetCreateMilestone ?? null;
1993
2053
  },
1994
- skip: !workspaceId || !enabled,
1995
- errorPolicy: "all"
1996
- });
2054
+ [createMutation, refetch]
2055
+ );
1997
2056
  const milestones = useMemo(
1998
2057
  () => data?.planmagnetMilestones?.items ?? [],
1999
2058
  [data]
@@ -2010,7 +2069,7 @@ function useMilestones(workspaceId, opts = {}) {
2010
2069
  }
2011
2070
  return map;
2012
2071
  }, [milestones]);
2013
- return { milestones, byDay, loading };
2072
+ return { milestones, byDay, loading, creating, createMilestone };
2014
2073
  }
2015
2074
  var WEEK_OPTS = { weekStartsOn: 1 };
2016
2075
  function dayKey(d) {
@@ -2026,18 +2085,49 @@ function TaskTimelineCore({
2026
2085
  onSelect,
2027
2086
  resolveAssignee,
2028
2087
  showMilestones = true,
2088
+ canCreateMilestone = false,
2089
+ milestoneProjectId,
2029
2090
  className,
2030
2091
  workspaceId: workspaceIdProp
2031
2092
  }) {
2032
2093
  const ctx = usePlanMagnetContextSafe();
2033
2094
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
2034
2095
  const L = useTaskLabels();
2035
- const { milestones, byDay: milestonesByDay } = useMilestones(workspaceId, {
2096
+ const {
2097
+ milestones,
2098
+ byDay: milestonesByDay,
2099
+ creating: creatingMilestone,
2100
+ createMilestone
2101
+ } = useMilestones(workspaceId, {
2036
2102
  projectId,
2037
2103
  enabled: showMilestones
2038
2104
  });
2039
2105
  const [mode, setMode] = useState(defaultMode);
2040
2106
  const [offset, setOffset] = useState(0);
2107
+ const milestoneProject = milestoneProjectId ?? projectId;
2108
+ const canAddMilestone = canCreateMilestone && Boolean(milestoneProject);
2109
+ const [showMilestoneForm, setShowMilestoneForm] = useState(false);
2110
+ const [milestoneName, setMilestoneName] = useState("");
2111
+ const [milestoneDue, setMilestoneDue] = useState("");
2112
+ const [milestoneError, setMilestoneError] = useState(null);
2113
+ const submitMilestone = async (e) => {
2114
+ e.preventDefault();
2115
+ const name = milestoneName.trim();
2116
+ if (!name || !milestoneProject) return;
2117
+ setMilestoneError(null);
2118
+ try {
2119
+ await createMilestone({
2120
+ projectId: milestoneProject,
2121
+ name,
2122
+ dueDate: milestoneDue || void 0
2123
+ });
2124
+ setMilestoneName("");
2125
+ setMilestoneDue("");
2126
+ setShowMilestoneForm(false);
2127
+ } catch {
2128
+ setMilestoneError(L.failedToCreateMilestone);
2129
+ }
2130
+ };
2041
2131
  const { data, loading, error } = useQuery(
2042
2132
  LIST_WORK_ITEMS,
2043
2133
  {
@@ -2091,7 +2181,7 @@ function TaskTimelineCore({
2091
2181
  const od = [];
2092
2182
  const ud = [];
2093
2183
  for (const t of tasks) {
2094
- const d = parseDate(t.dueDate);
2184
+ const d = parseDueDate(t.dueDate);
2095
2185
  if (!d) {
2096
2186
  ud.push(t);
2097
2187
  continue;
@@ -2185,12 +2275,82 @@ function TaskTimelineCore({
2185
2275
  t.id
2186
2276
  )) })
2187
2277
  ] }),
2188
- milestones.length > 0 && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2", children: [
2189
- /* @__PURE__ */ jsxs("p", { className: "mb-1 flex items-center gap-1 text-xs font-semibold text-primary", children: [
2190
- /* @__PURE__ */ jsx(Flag, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2191
- L.milestones
2278
+ (milestones.length > 0 || canAddMilestone) && /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-primary/30 bg-primary/5 px-3 py-2", children: [
2279
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 flex items-center justify-between gap-2", children: [
2280
+ /* @__PURE__ */ jsxs("p", { className: "flex items-center gap-1 text-xs font-semibold text-primary", children: [
2281
+ /* @__PURE__ */ jsx(Flag, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
2282
+ L.milestones
2283
+ ] }),
2284
+ canAddMilestone && /* @__PURE__ */ jsx(
2285
+ "button",
2286
+ {
2287
+ type: "button",
2288
+ onClick: () => {
2289
+ setShowMilestoneForm((v) => !v);
2290
+ setMilestoneError(null);
2291
+ },
2292
+ "aria-label": L.addMilestone,
2293
+ "aria-expanded": showMilestoneForm,
2294
+ title: L.addMilestone,
2295
+ className: "flex h-6 w-6 items-center justify-center rounded-md border border-primary/40 text-primary hover:bg-primary/10",
2296
+ children: /* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
2297
+ }
2298
+ )
2192
2299
  ] }),
2193
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: milestones.map((m) => /* @__PURE__ */ jsx(MilestoneFlag, { milestone: m }, m.id)) })
2300
+ showMilestoneForm && canAddMilestone && /* @__PURE__ */ jsxs(
2301
+ "form",
2302
+ {
2303
+ onSubmit: submitMilestone,
2304
+ className: "mb-2 flex flex-wrap items-center gap-2",
2305
+ children: [
2306
+ /* @__PURE__ */ jsx(
2307
+ "input",
2308
+ {
2309
+ type: "text",
2310
+ value: milestoneName,
2311
+ onChange: (e) => setMilestoneName(e.target.value),
2312
+ placeholder: L.milestoneNamePlaceholder,
2313
+ "aria-label": L.milestoneNamePlaceholder,
2314
+ autoFocus: true,
2315
+ className: "h-8 min-w-40 flex-1 rounded-md border border-border bg-background px-2 text-xs text-foreground"
2316
+ }
2317
+ ),
2318
+ /* @__PURE__ */ jsx(
2319
+ "input",
2320
+ {
2321
+ type: "date",
2322
+ value: milestoneDue,
2323
+ onChange: (e) => setMilestoneDue(e.target.value),
2324
+ "aria-label": L.dueDateLabel,
2325
+ className: "h-8 rounded-md border border-border bg-background px-2 text-xs text-foreground"
2326
+ }
2327
+ ),
2328
+ /* @__PURE__ */ jsx(
2329
+ "button",
2330
+ {
2331
+ type: "submit",
2332
+ disabled: !milestoneName.trim() || creatingMilestone,
2333
+ className: "h-8 rounded-md bg-primary px-3 text-xs font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
2334
+ children: creatingMilestone ? L.creating : L.addMilestone
2335
+ }
2336
+ ),
2337
+ /* @__PURE__ */ jsx(
2338
+ "button",
2339
+ {
2340
+ type: "button",
2341
+ onClick: () => {
2342
+ setShowMilestoneForm(false);
2343
+ setMilestoneError(null);
2344
+ },
2345
+ className: "h-8 rounded-md border border-border px-3 text-xs font-medium text-foreground hover:bg-muted/50",
2346
+ children: L.cancel
2347
+ }
2348
+ )
2349
+ ]
2350
+ }
2351
+ ),
2352
+ milestoneError && /* @__PURE__ */ jsx("p", { className: "mb-1 text-[11px] text-destructive", children: milestoneError }),
2353
+ milestones.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: milestones.map((m) => /* @__PURE__ */ jsx(MilestoneFlag, { milestone: m }, m.id)) })
2194
2354
  ] }),
2195
2355
  mode === "week" ? /* @__PURE__ */ jsx("div", { className: "grid grid-cols-7 gap-2", children: days.map((day) => {
2196
2356
  const items = byDay.get(dayKey(day)) ?? [];
@@ -2622,6 +2782,7 @@ function TaskCommentsCore({
2622
2782
  currentActorId,
2623
2783
  resolveAssignee,
2624
2784
  members,
2785
+ onMention,
2625
2786
  readonly,
2626
2787
  className
2627
2788
  }) {
@@ -2671,10 +2832,10 @@ function TaskCommentsCore({
2671
2832
  DELETE_ENTITY_COMMENT
2672
2833
  );
2673
2834
  const authorName = (id) => id === currentActorId ? L.you : resolveAssignee?.(id)?.name ?? void 0;
2674
- const post = async ({ text }, parentCommentId) => {
2835
+ const post = async ({ text, mentionUserIds }, parentCommentId) => {
2675
2836
  setError(null);
2676
2837
  try {
2677
- await createComment({
2838
+ const res = await createComment({
2678
2839
  variables: {
2679
2840
  input: {
2680
2841
  entityType,
@@ -2687,6 +2848,15 @@ function TaskCommentsCore({
2687
2848
  });
2688
2849
  await refetch();
2689
2850
  setReplyingTo(null);
2851
+ const toNotify = mentionUserIds.filter((id) => id !== currentActorId);
2852
+ if (toNotify.length && onMention) {
2853
+ onMention({
2854
+ mentionedIds: toNotify,
2855
+ text,
2856
+ entityId,
2857
+ commentId: res.data?.createEntityComment?.id ?? ""
2858
+ });
2859
+ }
2690
2860
  } catch (err) {
2691
2861
  setError(L.failedToComment);
2692
2862
  throw err;
@@ -2810,6 +2980,7 @@ function TaskDetailCore({
2810
2980
  onAddSubtask,
2811
2981
  commentsEntityType,
2812
2982
  currentActorId,
2983
+ onMention,
2813
2984
  className
2814
2985
  }) {
2815
2986
  const client = useApolloClient();
@@ -3010,7 +3181,7 @@ function TaskDetailCore({
3010
3181
  ),
3011
3182
  /* @__PURE__ */ jsx("span", { className: "text-foreground", children: resolveAssignee?.(item.assigneeId)?.name ?? L.assigned })
3012
3183
  ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: L.unassigned }) }),
3013
- !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDateFull(item.dueDate) ?? "\u2014" }) }),
3184
+ !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDueDateFull(item.dueDate) ?? "\u2014" }) }),
3014
3185
  !hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: L.storyPointsLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
3015
3186
  !hidden.has("description") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3016
3187
  /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.description }),
@@ -3111,7 +3282,8 @@ function TaskDetailCore({
3111
3282
  entityId: item.id,
3112
3283
  currentActorId,
3113
3284
  resolveAssignee,
3114
- members: assigneeOptions
3285
+ members: assigneeOptions,
3286
+ onMention
3115
3287
  }
3116
3288
  ) })
3117
3289
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSave, className: "space-y-4", children: [
@@ -3317,6 +3489,7 @@ function TasksPageCore({
3317
3489
  boardProjectId,
3318
3490
  columns,
3319
3491
  readonly,
3492
+ canCreateMilestone,
3320
3493
  hiddenFields,
3321
3494
  views,
3322
3495
  defaultView = "board",
@@ -3327,6 +3500,7 @@ function TasksPageCore({
3327
3500
  assigneeOptions,
3328
3501
  currentActorId,
3329
3502
  commentsEntityType,
3503
+ onMention,
3330
3504
  labels,
3331
3505
  openItemId,
3332
3506
  header,
@@ -3430,7 +3604,8 @@ function TasksPageCore({
3430
3604
  onOpenItem: handleSelect,
3431
3605
  onAddSubtask: handleAddSubtask,
3432
3606
  commentsEntityType,
3433
- currentActorId
3607
+ currentActorId,
3608
+ onMention
3434
3609
  }
3435
3610
  ) : null;
3436
3611
  const listPriority = priority === "ALL" ? void 0 : priority;
@@ -3512,7 +3687,9 @@ function TasksPageCore({
3512
3687
  search,
3513
3688
  assigneeId: mineAssignee,
3514
3689
  resolveAssignee,
3515
- onSelect: handleSelect
3690
+ onSelect: handleSelect,
3691
+ canCreateMilestone: canCreateMilestone ?? !readonly,
3692
+ milestoneProjectId: boardProjectId ?? projectId
3516
3693
  }
3517
3694
  ),
3518
3695
  showInlineDetail && /* @__PURE__ */ jsx("div", { className: "pb-6", children: detailView }),