@burdenoff/planmagnet-fe-sdk 2026.705.1 → 2026.706.1

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.js CHANGED
@@ -1,4 +1,4 @@
1
- import { createContext, useMemo, useEffect, useContext, useState, useCallback } from 'react';
1
+ import { createContext, useMemo, useEffect, useContext, useState, useCallback, useRef } from 'react';
2
2
  import { ApolloProvider, useQuery, useApolloClient, useMutation } from '@apollo/client/react';
3
3
  import { createHttpLink, ApolloClient, from, InMemoryCache } from '@apollo/client/core';
4
4
  import { setContext } from '@apollo/client/link/context';
@@ -512,6 +512,50 @@ var DELETE_WORK_ITEM = gql`
512
512
  planmagnetDeleteWorkItem(id: $id)
513
513
  }
514
514
  `;
515
+ var ENTITY_COMMENT_FIELDS = gql`
516
+ fragment EntityCommentFields on EntityComment {
517
+ id
518
+ authorId
519
+ authorType
520
+ content {
521
+ text
522
+ }
523
+ createdAt
524
+ edited
525
+ editedAt
526
+ status
527
+ parentCommentId
528
+ threadRootId
529
+ threadLevel
530
+ mentions {
531
+ mentionedUserId
532
+ }
533
+ }
534
+ `;
535
+ var GET_ENTITY_COMMENTS = gql`
536
+ query GetEntityComments($input: GetCommentsInput!) {
537
+ getEntityComments(input: $input) {
538
+ items {
539
+ ...EntityCommentFields
540
+ }
541
+ total
542
+ }
543
+ }
544
+ ${ENTITY_COMMENT_FIELDS}
545
+ `;
546
+ var CREATE_ENTITY_COMMENT = gql`
547
+ mutation CreateEntityComment($input: CreateEntityCommentInput!) {
548
+ createEntityComment(input: $input) {
549
+ ...EntityCommentFields
550
+ }
551
+ }
552
+ ${ENTITY_COMMENT_FIELDS}
553
+ `;
554
+ var DELETE_ENTITY_COMMENT = gql`
555
+ mutation DeleteEntityComment($input: DeleteCommentInput!) {
556
+ deleteEntityComment(input: $input)
557
+ }
558
+ `;
515
559
  function cn(...inputs) {
516
560
  return twMerge(clsx(inputs));
517
561
  }
@@ -751,6 +795,18 @@ var DEFAULT_TASK_LABELS = {
751
795
  parentLabel: "Parent",
752
796
  subtasksLabel: "Sub-tasks",
753
797
  addSubtask: "Add sub-task",
798
+ commentsLabel: "Comments",
799
+ commentPlaceholder: "Write a comment\u2026",
800
+ postComment: "Comment",
801
+ posting: "Posting\u2026",
802
+ noComments: "No comments yet",
803
+ you: "You",
804
+ editedTag: "(edited)",
805
+ failedToComment: "Failed to post comment",
806
+ failedToDeleteComment: "Failed to delete comment",
807
+ reply: "Reply",
808
+ replyPlaceholder: "Write a reply\u2026",
809
+ repliesCount: "{n} replies",
754
810
  create: "Create task",
755
811
  creating: "Creating\u2026",
756
812
  cancel: "Cancel",
@@ -2455,7 +2511,281 @@ function TaskToolbar({
2455
2511
  );
2456
2512
  }
2457
2513
  var fieldClass2 = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring";
2514
+ function CommentComposer({
2515
+ placeholder,
2516
+ submitLabel,
2517
+ pendingLabel,
2518
+ members,
2519
+ autoFocus,
2520
+ onSubmit
2521
+ }) {
2522
+ const [text, setText] = useState("");
2523
+ const [pending, setPending] = useState(false);
2524
+ const [mentioned, setMentioned] = useState([]);
2525
+ const [query, setQuery] = useState(null);
2526
+ const taRef = useRef(null);
2527
+ const suggestions = useMemo(() => {
2528
+ if (query == null || !members || members.length === 0) return [];
2529
+ const q = query.toLowerCase();
2530
+ return members.filter((m) => m.name.toLowerCase().includes(q)).slice(0, 6);
2531
+ }, [query, members]);
2532
+ const detectMention = (value) => {
2533
+ const caret = taRef.current?.selectionStart ?? value.length;
2534
+ const m = /(?:^|\s)@([\p{L}\p{N} ._-]*)$/u.exec(value.slice(0, caret));
2535
+ setQuery(m ? m[1] : null);
2536
+ };
2537
+ const onChange = (value) => {
2538
+ setText(value);
2539
+ detectMention(value);
2540
+ };
2541
+ const pickMention = (member) => {
2542
+ const el = taRef.current;
2543
+ const caret = el?.selectionStart ?? text.length;
2544
+ const before = text.slice(0, caret);
2545
+ const after = text.slice(caret);
2546
+ const replaced = before.replace(
2547
+ /@([\p{L}\p{N} ._-]*)$/u,
2548
+ `@${member.name} `
2549
+ );
2550
+ setText(replaced + after);
2551
+ setQuery(null);
2552
+ setMentioned(
2553
+ (prev) => prev.some((p) => p.id === member.id) ? prev : [...prev, member]
2554
+ );
2555
+ requestAnimationFrame(() => el?.focus());
2556
+ };
2557
+ const submit = async (e) => {
2558
+ e.preventDefault();
2559
+ const body = text.trim();
2560
+ if (!body || pending) return;
2561
+ const ids = mentioned.filter((m) => body.includes(`@${m.name}`)).map((m) => m.id);
2562
+ setPending(true);
2563
+ try {
2564
+ await onSubmit({ text: body, mentionUserIds: ids });
2565
+ setText("");
2566
+ setMentioned([]);
2567
+ setQuery(null);
2568
+ } finally {
2569
+ setPending(false);
2570
+ }
2571
+ };
2572
+ return /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "relative space-y-2", children: [
2573
+ /* @__PURE__ */ jsx(
2574
+ "textarea",
2575
+ {
2576
+ ref: taRef,
2577
+ className: cn(fieldClass2, "min-h-[60px]"),
2578
+ rows: 2,
2579
+ value: text,
2580
+ autoFocus,
2581
+ onChange: (e) => onChange(e.target.value),
2582
+ onKeyUp: (e) => detectMention(e.target.value),
2583
+ onClick: (e) => detectMention(e.target.value),
2584
+ placeholder,
2585
+ "aria-label": placeholder
2586
+ }
2587
+ ),
2588
+ suggestions.length > 0 && /* @__PURE__ */ jsx("ul", { className: "absolute left-0 top-full z-20 mt-1 max-h-48 w-56 overflow-auto rounded-md border border-border bg-popover p-1 shadow-md", children: suggestions.map((m) => /* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsxs(
2589
+ "button",
2590
+ {
2591
+ type: "button",
2592
+ onMouseDown: (e) => {
2593
+ e.preventDefault();
2594
+ pickMention(m);
2595
+ },
2596
+ className: "flex w-full items-center rounded px-2 py-1 text-left text-sm text-foreground hover:bg-muted",
2597
+ children: [
2598
+ "@",
2599
+ m.name
2600
+ ]
2601
+ }
2602
+ ) }, m.id)) }),
2603
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx(
2604
+ "button",
2605
+ {
2606
+ type: "submit",
2607
+ disabled: pending || !text.trim(),
2608
+ className: cn(
2609
+ "rounded-md bg-primary px-3 py-1.5 text-sm font-medium text-primary-foreground",
2610
+ (pending || !text.trim()) && "cursor-not-allowed opacity-50"
2611
+ ),
2612
+ children: pending ? pendingLabel : submitLabel
2613
+ }
2614
+ ) })
2615
+ ] });
2616
+ }
2458
2617
  var labelClass2 = "text-xs font-medium uppercase tracking-wide text-muted-foreground";
2618
+ var escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2619
+ function TaskCommentsCore({
2620
+ entityType,
2621
+ entityId,
2622
+ currentActorId,
2623
+ resolveAssignee,
2624
+ members,
2625
+ readonly,
2626
+ className
2627
+ }) {
2628
+ const L = useTaskLabels();
2629
+ const [error, setError] = useState(null);
2630
+ const [replyingTo, setReplyingTo] = useState(null);
2631
+ const { data, loading, refetch } = useQuery(
2632
+ GET_ENTITY_COMMENTS,
2633
+ {
2634
+ // No parentCommentId → the service returns EVERY comment (all levels)
2635
+ // flat; we build the thread tree client-side via threadRootId.
2636
+ variables: {
2637
+ input: {
2638
+ entityType,
2639
+ entityId,
2640
+ take: 200,
2641
+ sortBy: "createdAt",
2642
+ sortOrder: "asc"
2643
+ }
2644
+ },
2645
+ skip: !entityId,
2646
+ errorPolicy: "all"
2647
+ }
2648
+ );
2649
+ const all = data?.getEntityComments?.items ?? [];
2650
+ const { topLevel, repliesByRoot } = useMemo(() => {
2651
+ const top = [];
2652
+ const map = /* @__PURE__ */ new Map();
2653
+ for (const c of all) {
2654
+ if (!c.parentCommentId) {
2655
+ top.push(c);
2656
+ } else {
2657
+ const root = c.threadRootId ?? c.parentCommentId;
2658
+ const arr = map.get(root);
2659
+ if (arr) arr.push(c);
2660
+ else map.set(root, [c]);
2661
+ }
2662
+ }
2663
+ return { topLevel: top, repliesByRoot: map };
2664
+ }, [all]);
2665
+ const [createComment] = useMutation(
2666
+ CREATE_ENTITY_COMMENT
2667
+ );
2668
+ const [deleteComment] = useMutation(
2669
+ DELETE_ENTITY_COMMENT
2670
+ );
2671
+ const authorName = (id) => id === currentActorId ? L.you : resolveAssignee?.(id)?.name ?? void 0;
2672
+ const post = async ({ text, mentionUserIds }, parentCommentId) => {
2673
+ setError(null);
2674
+ try {
2675
+ await createComment({
2676
+ variables: {
2677
+ input: {
2678
+ entityType,
2679
+ entityId,
2680
+ content: { text },
2681
+ contentType: "TEXT",
2682
+ ...parentCommentId ? { parentCommentId } : {},
2683
+ ...mentionUserIds.length ? { mentionUserIds } : {}
2684
+ }
2685
+ }
2686
+ });
2687
+ await refetch();
2688
+ setReplyingTo(null);
2689
+ } catch (err) {
2690
+ setError(L.failedToComment);
2691
+ throw err;
2692
+ }
2693
+ };
2694
+ const handleDelete = async (id) => {
2695
+ setError(null);
2696
+ try {
2697
+ await deleteComment({ variables: { input: { id, soft: true } } });
2698
+ await refetch();
2699
+ } catch {
2700
+ setError(L.failedToDeleteComment);
2701
+ }
2702
+ };
2703
+ const renderBody = (c) => {
2704
+ const text = c.content?.text ?? "";
2705
+ const names = (c.mentions ?? []).map((m) => resolveAssignee?.(m.mentionedUserId)?.name).filter((n) => Boolean(n));
2706
+ if (names.length === 0) return text;
2707
+ const tokens = new Set(names.map((n) => `@${n}`));
2708
+ const re = new RegExp(`(@(?:${names.map(escapeRe).join("|")}))`, "g");
2709
+ return text.split(re).map(
2710
+ (part, i) => tokens.has(part) ? /* @__PURE__ */ jsx(
2711
+ "span",
2712
+ {
2713
+ className: "rounded bg-primary/10 px-0.5 font-medium text-primary",
2714
+ children: part
2715
+ },
2716
+ i
2717
+ ) : /* @__PURE__ */ jsx("span", { children: part }, i)
2718
+ );
2719
+ };
2720
+ const commentBlock = (c, isReply) => /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
2721
+ /* @__PURE__ */ jsx(Avatar, { id: c.authorId, name: authorName(c.authorId), size: "sm" }),
2722
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
2723
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2724
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: authorName(c.authorId) ?? c.authorId.slice(0, 8) }),
2725
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: formatDateFull(c.createdAt) }),
2726
+ c.edited && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: L.editedTag }),
2727
+ !readonly && c.authorId === currentActorId && /* @__PURE__ */ jsx(
2728
+ "button",
2729
+ {
2730
+ type: "button",
2731
+ onClick: () => handleDelete(c.id),
2732
+ "aria-label": L.del,
2733
+ title: L.del,
2734
+ className: "ml-auto rounded p-1 text-muted-foreground hover:text-destructive",
2735
+ children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" })
2736
+ }
2737
+ )
2738
+ ] }),
2739
+ /* @__PURE__ */ jsx("p", { className: "whitespace-pre-wrap break-words text-sm text-foreground", children: renderBody(c) }),
2740
+ !isReply && !readonly && /* @__PURE__ */ jsx(
2741
+ "button",
2742
+ {
2743
+ type: "button",
2744
+ onClick: () => setReplyingTo((prev) => prev === c.id ? null : c.id),
2745
+ className: "mt-1 text-xs font-medium text-muted-foreground hover:text-foreground",
2746
+ children: L.reply
2747
+ }
2748
+ )
2749
+ ] })
2750
+ ] });
2751
+ return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4", className), children: [
2752
+ /* @__PURE__ */ jsxs("div", { className: labelClass2, children: [
2753
+ L.commentsLabel,
2754
+ all.length > 0 ? ` (${all.length})` : ""
2755
+ ] }),
2756
+ /* @__PURE__ */ jsx("ul", { className: "space-y-4", children: topLevel.length === 0 && !loading ? /* @__PURE__ */ jsx("li", { className: "text-sm text-muted-foreground", children: L.noComments }) : topLevel.map((c) => {
2757
+ const replies = repliesByRoot.get(c.id) ?? [];
2758
+ return /* @__PURE__ */ jsxs("li", { className: "space-y-2", children: [
2759
+ commentBlock(c, false),
2760
+ replies.length > 0 && /* @__PURE__ */ jsx("ul", { className: "ml-8 space-y-2 border-l border-border pl-3", children: replies.map((r) => /* @__PURE__ */ jsx("li", { children: commentBlock(r, true) }, r.id)) }),
2761
+ !readonly && replyingTo === c.id && /* @__PURE__ */ jsx("div", { className: "ml-8", children: /* @__PURE__ */ jsx(
2762
+ CommentComposer,
2763
+ {
2764
+ placeholder: L.replyPlaceholder,
2765
+ submitLabel: L.reply,
2766
+ pendingLabel: L.posting,
2767
+ members,
2768
+ autoFocus: true,
2769
+ onSubmit: (p) => post(p, c.id)
2770
+ }
2771
+ ) })
2772
+ ] }, c.id);
2773
+ }) }),
2774
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-destructive", children: error }),
2775
+ !readonly && /* @__PURE__ */ jsx(
2776
+ CommentComposer,
2777
+ {
2778
+ placeholder: L.commentPlaceholder,
2779
+ submitLabel: L.postComment,
2780
+ pendingLabel: L.posting,
2781
+ members,
2782
+ onSubmit: (p) => post(p)
2783
+ }
2784
+ )
2785
+ ] });
2786
+ }
2787
+ var fieldClass3 = "w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring";
2788
+ var labelClass3 = "text-xs font-medium uppercase tracking-wide text-muted-foreground";
2459
2789
  var priorityVariant = {
2460
2790
  NONE: "muted",
2461
2791
  LOW: "muted",
@@ -2477,6 +2807,8 @@ function TaskDetailCore({
2477
2807
  renderLinkIcon,
2478
2808
  onOpenItem,
2479
2809
  onAddSubtask,
2810
+ commentsEntityType,
2811
+ currentActorId,
2480
2812
  className
2481
2813
  }) {
2482
2814
  const client = useApolloClient();
@@ -2661,119 +2993,134 @@ function TaskDetailCore({
2661
2993
  ": ",
2662
2994
  deleteError.message
2663
2995
  ] }),
2664
- !editing ? /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
2665
- !hidden.has("type") && /* @__PURE__ */ jsx(Field, { label: L.typeLabel, children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: L.type[item.type] }) }),
2666
- !hidden.has("status") && /* @__PURE__ */ jsx(Field, { label: L.statusLabel, children: /* @__PURE__ */ jsx("span", { className: "capitalize text-foreground", children: L.status[item.status] }) }),
2667
- !hidden.has("priority") && /* @__PURE__ */ jsx(Field, { label: L.priorityLabel, children: /* @__PURE__ */ jsx(Badge, { variant: priorityVariant[item.priority], children: L.priority[item.priority] }) }),
2668
- !hidden.has("assignee") && /* @__PURE__ */ jsx(Field, { label: L.assigneeLabel, children: item.assigneeId ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
2669
- /* @__PURE__ */ jsx(
2670
- Avatar,
2671
- {
2672
- id: item.assigneeId,
2673
- name: resolveAssignee?.(item.assigneeId)?.name,
2674
- size: "sm"
2675
- }
2676
- ),
2677
- /* @__PURE__ */ jsx("span", { className: "text-foreground", children: resolveAssignee?.(item.assigneeId)?.name ?? L.assigned })
2678
- ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: L.unassigned }) }),
2679
- !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDateFull(item.dueDate) ?? "\u2014" }) }),
2680
- !hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: L.storyPointsLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
2681
- !hidden.has("description") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2682
- /* @__PURE__ */ jsx("dt", { className: labelClass2, children: L.description }),
2683
- /* @__PURE__ */ jsx("dd", { className: "mt-1 whitespace-pre-wrap text-sm text-foreground", children: item.description || "\u2014" })
2684
- ] }),
2685
- !hidden.has("labels") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2686
- /* @__PURE__ */ jsx("dt", { className: labelClass2, children: L.labelsLabel }),
2687
- /* @__PURE__ */ jsx("dd", { className: "mt-1 flex flex-wrap gap-1", children: (item.labels ?? []).length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u2014" }) : item.labels.map((l) => /* @__PURE__ */ jsx(
2688
- "span",
2689
- {
2690
- className: "rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground",
2691
- children: l
2692
- },
2693
- l
2694
- )) })
2695
- ] }),
2696
- links.length > 0 && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2697
- /* @__PURE__ */ jsx("dt", { className: labelClass2, children: L.linkedRecordsLabel }),
2698
- /* @__PURE__ */ jsx("dd", { className: "mt-1 flex flex-wrap gap-1.5", children: links.map((link) => /* @__PURE__ */ jsx(
2699
- LinkChip,
2700
- {
2701
- link,
2702
- renderIcon: renderLinkIcon
2703
- },
2704
- link.id
2705
- )) })
2706
- ] }),
2707
- parent && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2708
- /* @__PURE__ */ jsx("dt", { className: labelClass2, children: L.parentLabel }),
2709
- /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsxs(
2710
- "button",
2711
- {
2712
- type: "button",
2713
- onClick: () => onOpenItem?.(parent.id),
2714
- className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-muted/40 px-2 py-1 text-sm text-foreground hover:bg-muted",
2715
- children: [
2716
- /* @__PURE__ */ jsx(StatusIcon, { status: parent.status }),
2717
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: parent.key }),
2718
- /* @__PURE__ */ jsx("span", { className: "truncate", children: parent.title })
2719
- ]
2720
- }
2721
- ) })
2722
- ] }),
2723
- (children.length > 0 || onAddSubtask && canEdit) && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
2724
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
2725
- /* @__PURE__ */ jsxs("dt", { className: labelClass2, children: [
2726
- L.subtasksLabel,
2727
- children.length > 0 ? ` (${children.length})` : ""
2728
- ] }),
2729
- onAddSubtask && canEdit && /* @__PURE__ */ jsxs(
2996
+ !editing ? /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
2997
+ /* @__PURE__ */ jsxs("dl", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
2998
+ !hidden.has("type") && /* @__PURE__ */ jsx(Field, { label: L.typeLabel, children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: L.type[item.type] }) }),
2999
+ !hidden.has("status") && /* @__PURE__ */ jsx(Field, { label: L.statusLabel, children: /* @__PURE__ */ jsx("span", { className: "capitalize text-foreground", children: L.status[item.status] }) }),
3000
+ !hidden.has("priority") && /* @__PURE__ */ jsx(Field, { label: L.priorityLabel, children: /* @__PURE__ */ jsx(Badge, { variant: priorityVariant[item.priority], children: L.priority[item.priority] }) }),
3001
+ !hidden.has("assignee") && /* @__PURE__ */ jsx(Field, { label: L.assigneeLabel, children: item.assigneeId ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1.5", children: [
3002
+ /* @__PURE__ */ jsx(
3003
+ Avatar,
3004
+ {
3005
+ id: item.assigneeId,
3006
+ name: resolveAssignee?.(item.assigneeId)?.name,
3007
+ size: "sm"
3008
+ }
3009
+ ),
3010
+ /* @__PURE__ */ jsx("span", { className: "text-foreground", children: resolveAssignee?.(item.assigneeId)?.name ?? L.assigned })
3011
+ ] }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: L.unassigned }) }),
3012
+ !hidden.has("dueDate") && /* @__PURE__ */ jsx(Field, { label: L.dueDateLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: formatDateFull(item.dueDate) ?? "\u2014" }) }),
3013
+ !hidden.has("storyPoints") && /* @__PURE__ */ jsx(Field, { label: L.storyPointsLabel, children: /* @__PURE__ */ jsx("span", { className: "text-foreground", children: item.storyPoints ?? 0 }) }),
3014
+ !hidden.has("description") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3015
+ /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.description }),
3016
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 whitespace-pre-wrap text-sm text-foreground", children: item.description || "\u2014" })
3017
+ ] }),
3018
+ !hidden.has("labels") && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3019
+ /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.labelsLabel }),
3020
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 flex flex-wrap gap-1", children: (item.labels ?? []).length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u2014" }) : item.labels.map((l) => /* @__PURE__ */ jsx(
3021
+ "span",
3022
+ {
3023
+ className: "rounded bg-muted px-1.5 py-0.5 text-xs text-muted-foreground",
3024
+ children: l
3025
+ },
3026
+ l
3027
+ )) })
3028
+ ] }),
3029
+ links.length > 0 && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3030
+ /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.linkedRecordsLabel }),
3031
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 flex flex-wrap gap-1.5", children: links.map((link) => /* @__PURE__ */ jsx(
3032
+ LinkChip,
3033
+ {
3034
+ link,
3035
+ renderIcon: renderLinkIcon
3036
+ },
3037
+ link.id
3038
+ )) })
3039
+ ] }),
3040
+ parent && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3041
+ /* @__PURE__ */ jsx("dt", { className: labelClass3, children: L.parentLabel }),
3042
+ /* @__PURE__ */ jsx("dd", { className: "mt-1", children: /* @__PURE__ */ jsxs(
2730
3043
  "button",
2731
3044
  {
2732
3045
  type: "button",
2733
- onClick: () => onAddSubtask({ id: item.id, projectId: item.projectId }),
2734
- className: "text-xs font-medium text-primary hover:underline",
3046
+ onClick: () => onOpenItem?.(parent.id),
3047
+ className: "inline-flex max-w-full items-center gap-1.5 rounded-md border border-border bg-muted/40 px-2 py-1 text-sm text-foreground hover:bg-muted",
2735
3048
  children: [
2736
- "+ ",
2737
- L.addSubtask
3049
+ /* @__PURE__ */ jsx(StatusIcon, { status: parent.status }),
3050
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: parent.key }),
3051
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: parent.title })
2738
3052
  ]
2739
3053
  }
2740
- )
3054
+ ) })
2741
3055
  ] }),
2742
- /* @__PURE__ */ jsx("dd", { className: "mt-1 space-y-1", children: children.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u2014" }) : children.map((c) => /* @__PURE__ */ jsxs(
2743
- "button",
2744
- {
2745
- type: "button",
2746
- onClick: () => onOpenItem?.(c.id),
2747
- className: "flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-sm hover:bg-muted",
2748
- children: [
2749
- /* @__PURE__ */ jsx(StatusIcon, { status: c.status }),
2750
- /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: c.key }),
2751
- /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-foreground", children: c.title })
2752
- ]
2753
- },
2754
- c.id
2755
- )) })
2756
- ] }),
2757
- (item.createdAt || item.updatedAt) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-1 border-t border-border pt-3 text-xs text-muted-foreground sm:col-span-2", children: [
2758
- item.createdAt && /* @__PURE__ */ jsxs("span", { children: [
2759
- L.createdLabel,
2760
- " ",
2761
- formatDateFull(item.createdAt)
3056
+ (children.length > 0 || onAddSubtask && canEdit) && /* @__PURE__ */ jsxs("div", { className: "sm:col-span-2", children: [
3057
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
3058
+ /* @__PURE__ */ jsxs("dt", { className: labelClass3, children: [
3059
+ L.subtasksLabel,
3060
+ children.length > 0 ? ` (${children.length})` : ""
3061
+ ] }),
3062
+ onAddSubtask && canEdit && /* @__PURE__ */ jsxs(
3063
+ "button",
3064
+ {
3065
+ type: "button",
3066
+ onClick: () => onAddSubtask({
3067
+ id: item.id,
3068
+ projectId: item.projectId
3069
+ }),
3070
+ className: "text-xs font-medium text-primary hover:underline",
3071
+ children: [
3072
+ "+ ",
3073
+ L.addSubtask
3074
+ ]
3075
+ }
3076
+ )
3077
+ ] }),
3078
+ /* @__PURE__ */ jsx("dd", { className: "mt-1 space-y-1", children: children.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-sm text-muted-foreground", children: "\u2014" }) : children.map((c) => /* @__PURE__ */ jsxs(
3079
+ "button",
3080
+ {
3081
+ type: "button",
3082
+ onClick: () => onOpenItem?.(c.id),
3083
+ className: "flex w-full items-center gap-2 rounded-md px-2 py-1 text-left text-sm hover:bg-muted",
3084
+ children: [
3085
+ /* @__PURE__ */ jsx(StatusIcon, { status: c.status }),
3086
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: c.key }),
3087
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate text-foreground", children: c.title })
3088
+ ]
3089
+ },
3090
+ c.id
3091
+ )) })
2762
3092
  ] }),
2763
- item.updatedAt && /* @__PURE__ */ jsxs("span", { children: [
2764
- L.updatedLabel,
2765
- " ",
2766
- formatDateFull(item.updatedAt)
3093
+ (item.createdAt || item.updatedAt) && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-x-6 gap-y-1 border-t border-border pt-3 text-xs text-muted-foreground sm:col-span-2", children: [
3094
+ item.createdAt && /* @__PURE__ */ jsxs("span", { children: [
3095
+ L.createdLabel,
3096
+ " ",
3097
+ formatDateFull(item.createdAt)
3098
+ ] }),
3099
+ item.updatedAt && /* @__PURE__ */ jsxs("span", { children: [
3100
+ L.updatedLabel,
3101
+ " ",
3102
+ formatDateFull(item.updatedAt)
3103
+ ] })
2767
3104
  ] })
2768
- ] })
3105
+ ] }),
3106
+ commentsEntityType && /* @__PURE__ */ jsx("div", { className: "border-t border-border pt-4", children: /* @__PURE__ */ jsx(
3107
+ TaskCommentsCore,
3108
+ {
3109
+ entityType: commentsEntityType,
3110
+ entityId: item.id,
3111
+ currentActorId,
3112
+ resolveAssignee,
3113
+ members: assigneeOptions
3114
+ }
3115
+ ) })
2769
3116
  ] }) : /* @__PURE__ */ jsxs("form", { onSubmit: handleSave, className: "space-y-4", children: [
2770
3117
  /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2771
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-title", className: labelClass2, children: L.title }),
3118
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-title", className: labelClass3, children: L.title }),
2772
3119
  /* @__PURE__ */ jsx(
2773
3120
  "input",
2774
3121
  {
2775
3122
  id: "pm-detail-title",
2776
- className: fieldClass2,
3123
+ className: fieldClass3,
2777
3124
  value: title,
2778
3125
  onChange: (e) => setTitle(e.target.value),
2779
3126
  required: true
@@ -2781,12 +3128,12 @@ function TaskDetailCore({
2781
3128
  )
2782
3129
  ] }),
2783
3130
  !hidden.has("description") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2784
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-desc", className: labelClass2, children: L.description }),
3131
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-desc", className: labelClass3, children: L.description }),
2785
3132
  /* @__PURE__ */ jsx(
2786
3133
  "textarea",
2787
3134
  {
2788
3135
  id: "pm-detail-desc",
2789
- className: cn(fieldClass2, "min-h-[80px]"),
3136
+ className: cn(fieldClass3, "min-h-[80px]"),
2790
3137
  value: description,
2791
3138
  onChange: (e) => setDescription(e.target.value)
2792
3139
  }
@@ -2794,12 +3141,12 @@ function TaskDetailCore({
2794
3141
  ] }),
2795
3142
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [
2796
3143
  !hidden.has("type") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2797
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-type", className: labelClass2, children: L.typeLabel }),
3144
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-type", className: labelClass3, children: L.typeLabel }),
2798
3145
  /* @__PURE__ */ jsx(
2799
3146
  "select",
2800
3147
  {
2801
3148
  id: "pm-detail-type",
2802
- className: cn(fieldClass2, "capitalize"),
3149
+ className: cn(fieldClass3, "capitalize"),
2803
3150
  value: type,
2804
3151
  onChange: (e) => setType(e.target.value),
2805
3152
  children: TASK_TYPES.map((t) => /* @__PURE__ */ jsx("option", { value: t, children: L.type[t] }, t))
@@ -2807,12 +3154,12 @@ function TaskDetailCore({
2807
3154
  )
2808
3155
  ] }),
2809
3156
  !hidden.has("status") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2810
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-status", className: labelClass2, children: L.statusLabel }),
3157
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-status", className: labelClass3, children: L.statusLabel }),
2811
3158
  /* @__PURE__ */ jsx(
2812
3159
  "select",
2813
3160
  {
2814
3161
  id: "pm-detail-status",
2815
- className: cn(fieldClass2, "capitalize"),
3162
+ className: cn(fieldClass3, "capitalize"),
2816
3163
  value: status,
2817
3164
  onChange: (e) => setStatus(e.target.value),
2818
3165
  children: TASK_STATUSES.map((s) => /* @__PURE__ */ jsx("option", { value: s, children: L.status[s] }, s))
@@ -2820,12 +3167,12 @@ function TaskDetailCore({
2820
3167
  )
2821
3168
  ] }),
2822
3169
  !hidden.has("priority") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2823
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-priority", className: labelClass2, children: L.priorityLabel }),
3170
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-priority", className: labelClass3, children: L.priorityLabel }),
2824
3171
  /* @__PURE__ */ jsx(
2825
3172
  "select",
2826
3173
  {
2827
3174
  id: "pm-detail-priority",
2828
- className: cn(fieldClass2, "capitalize"),
3175
+ className: cn(fieldClass3, "capitalize"),
2829
3176
  value: priority,
2830
3177
  onChange: (e) => setPriority(e.target.value),
2831
3178
  children: TASK_PRIORITIES.map((p) => /* @__PURE__ */ jsx("option", { value: p, children: L.priority[p] }, p))
@@ -2833,12 +3180,12 @@ function TaskDetailCore({
2833
3180
  )
2834
3181
  ] }),
2835
3182
  !hidden.has("assignee") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2836
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-assignee", className: labelClass2, children: L.assigneeLabel }),
3183
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-assignee", className: labelClass3, children: L.assigneeLabel }),
2837
3184
  assigneeOptions && assigneeOptions.length > 0 ? /* @__PURE__ */ jsxs(
2838
3185
  "select",
2839
3186
  {
2840
3187
  id: "pm-detail-assignee",
2841
- className: fieldClass2,
3188
+ className: fieldClass3,
2842
3189
  value: assigneeId,
2843
3190
  onChange: (e) => setAssigneeId(e.target.value),
2844
3191
  children: [
@@ -2850,7 +3197,7 @@ function TaskDetailCore({
2850
3197
  "input",
2851
3198
  {
2852
3199
  id: "pm-detail-assignee",
2853
- className: fieldClass2,
3200
+ className: fieldClass3,
2854
3201
  value: assigneeId,
2855
3202
  onChange: (e) => setAssigneeId(e.target.value),
2856
3203
  placeholder: L.assigneePlaceholder
@@ -2858,27 +3205,27 @@ function TaskDetailCore({
2858
3205
  )
2859
3206
  ] }),
2860
3207
  !hidden.has("dueDate") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2861
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-due", className: labelClass2, children: L.dueDateLabel }),
3208
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-due", className: labelClass3, children: L.dueDateLabel }),
2862
3209
  /* @__PURE__ */ jsx(
2863
3210
  "input",
2864
3211
  {
2865
3212
  id: "pm-detail-due",
2866
3213
  type: "date",
2867
- className: fieldClass2,
3214
+ className: fieldClass3,
2868
3215
  value: dueDate,
2869
3216
  onChange: (e) => setDueDate(e.target.value)
2870
3217
  }
2871
3218
  )
2872
3219
  ] }),
2873
3220
  !hidden.has("storyPoints") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2874
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-points", className: labelClass2, children: L.storyPointsLabel }),
3221
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-points", className: labelClass3, children: L.storyPointsLabel }),
2875
3222
  /* @__PURE__ */ jsx(
2876
3223
  "input",
2877
3224
  {
2878
3225
  id: "pm-detail-points",
2879
3226
  type: "number",
2880
3227
  min: 0,
2881
- className: fieldClass2,
3228
+ className: fieldClass3,
2882
3229
  value: storyPoints,
2883
3230
  onChange: (e) => setStoryPoints(e.target.value)
2884
3231
  }
@@ -2886,12 +3233,12 @@ function TaskDetailCore({
2886
3233
  ] })
2887
3234
  ] }),
2888
3235
  !hidden.has("labels") && /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
2889
- /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-labels", className: labelClass2, children: L.labelsLabel }),
3236
+ /* @__PURE__ */ jsx("label", { htmlFor: "pm-detail-labels", className: labelClass3, children: L.labelsLabel }),
2890
3237
  /* @__PURE__ */ jsx(
2891
3238
  "input",
2892
3239
  {
2893
3240
  id: "pm-detail-labels",
2894
- className: fieldClass2,
3241
+ className: fieldClass3,
2895
3242
  value: labels,
2896
3243
  onChange: (e) => setLabels(e.target.value),
2897
3244
  placeholder: L.labelsPlaceholder
@@ -2931,7 +3278,7 @@ function TaskDetailCore({
2931
3278
  }
2932
3279
  function Field({ label, children }) {
2933
3280
  return /* @__PURE__ */ jsxs("div", { children: [
2934
- /* @__PURE__ */ jsx("dt", { className: labelClass2, children: label }),
3281
+ /* @__PURE__ */ jsx("dt", { className: labelClass3, children: label }),
2935
3282
  /* @__PURE__ */ jsx("dd", { className: "mt-1 text-sm", children })
2936
3283
  ] });
2937
3284
  }
@@ -2978,6 +3325,7 @@ function TasksPageCore({
2978
3325
  renderLinkIcon,
2979
3326
  assigneeOptions,
2980
3327
  currentActorId,
3328
+ commentsEntityType,
2981
3329
  labels,
2982
3330
  openItemId,
2983
3331
  header,
@@ -3079,7 +3427,9 @@ function TasksPageCore({
3079
3427
  onUpdated: handleUpdated,
3080
3428
  onDeleted: handleDeleted,
3081
3429
  onOpenItem: handleSelect,
3082
- onAddSubtask: handleAddSubtask
3430
+ onAddSubtask: handleAddSubtask,
3431
+ commentsEntityType,
3432
+ currentActorId
3083
3433
  }
3084
3434
  ) : null;
3085
3435
  const listPriority = priority === "ALL" ? void 0 : priority;
@@ -3215,6 +3565,6 @@ function TasksPage({
3215
3565
  );
3216
3566
  }
3217
3567
 
3218
- export { Avatar, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DEFAULT_TASK_LABELS, DELETE_WORK_ITEM, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_MILESTONES, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, MILESTONE_FIELDS, MOVE_WORK_ITEM, Modal, PRIORITY_META, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, PriorityIndicator, STATUS_META, StatusIcon, StatusPill, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskCard, TaskDetail, TaskDetailCore, TaskLabelsProvider, TaskList, TaskListCore, TaskTimelineCore, TaskToolbar, TasksPage, TasksPageCore, TypeBadge, UPDATE_WORK_ITEM, WORK_ITEM_FIELDS, WORK_ITEM_LINK_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, mergeTaskLabels, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, useMilestones, usePlanMagnetContext, usePlanMagnetContextSafe, useTaskLabels, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
3568
+ export { Avatar, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DEFAULT_TASK_LABELS, DELETE_WORK_ITEM, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_MILESTONES, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, MILESTONE_FIELDS, MOVE_WORK_ITEM, Modal, PRIORITY_META, PROJECT_SUMMARY_FIELDS, PlanMagnetProvider, PriorityIndicator, STATUS_META, StatusIcon, StatusPill, TASK_PRIORITIES, TASK_STATUSES, TASK_TYPES, TaskBoard, TaskBoardCore, TaskCard, TaskCommentsCore, TaskDetail, TaskDetailCore, TaskLabelsProvider, TaskList, TaskListCore, TaskTimelineCore, TaskToolbar, TasksPage, TasksPageCore, TypeBadge, UPDATE_WORK_ITEM, WORK_ITEM_FIELDS, WORK_ITEM_LINK_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, mergeTaskLabels, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, useMilestones, usePlanMagnetContext, usePlanMagnetContextSafe, useTaskLabels, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
3219
3569
  //# sourceMappingURL=index.js.map
3220
3570
  //# sourceMappingURL=index.js.map