@burdenoff/planmagnet-fe-sdk 2026.704.4 → 2026.705.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.d.ts CHANGED
@@ -810,6 +810,13 @@ interface TaskDetailCoreProps {
810
810
  linkSourceProduct?: string;
811
811
  /** Optional host hook to render an icon per linked-entity type. */
812
812
  renderLinkIcon?: RenderLinkIcon;
813
+ /** Open another work item's detail (parent link / sub-task row click). */
814
+ onOpenItem?: (id: string) => void;
815
+ /** Start creating a sub-task under this item (the "Add sub-task" affordance). */
816
+ onAddSubtask?: (parent: {
817
+ id: string;
818
+ projectId: string;
819
+ }) => void;
813
820
  /** Extra classes on the root container. */
814
821
  className?: string;
815
822
  }
@@ -818,7 +825,7 @@ interface TaskDetailCoreProps {
818
825
  * edit. Fetches via `planmagnetWorkItem(id)`, saves via
819
826
  * `planmagnetUpdateWorkItem`. BYO-Apollo, semantic-token only, farm-free.
820
827
  */
821
- declare function TaskDetailCore({ id, readonly, hiddenFields, hideHeader, onUpdated, onDeleted, onError, resolveAssignee, assigneeOptions, linkSourceProduct, renderLinkIcon, className, }: TaskDetailCoreProps): react.JSX.Element;
828
+ declare function TaskDetailCore({ id, readonly, hiddenFields, hideHeader, onUpdated, onDeleted, onError, resolveAssignee, assigneeOptions, linkSourceProduct, renderLinkIcon, onOpenItem, onAddSubtask, className, }: TaskDetailCoreProps): react.JSX.Element;
822
829
 
823
830
  interface TaskDetailProps extends TaskDetailCoreProps {
824
831
  /** Workspace API gateway URL (URL mode). */
@@ -918,6 +925,9 @@ interface TaskLabels {
918
925
  linkedRecordsLabel: string;
919
926
  createdLabel: string;
920
927
  updatedLabel: string;
928
+ parentLabel: string;
929
+ subtasksLabel: string;
930
+ addSubtask: string;
921
931
  create: string;
922
932
  creating: string;
923
933
  cancel: string;
package/dist/index.js CHANGED
@@ -748,6 +748,9 @@ var DEFAULT_TASK_LABELS = {
748
748
  linkedRecordsLabel: "Linked records",
749
749
  createdLabel: "Created",
750
750
  updatedLabel: "Updated",
751
+ parentLabel: "Parent",
752
+ subtasksLabel: "Sub-tasks",
753
+ addSubtask: "Add sub-task",
751
754
  create: "Create task",
752
755
  creating: "Creating\u2026",
753
756
  cancel: "Cancel",
@@ -1370,6 +1373,10 @@ function CreateTaskCore({
1370
1373
  projectId,
1371
1374
  title: title.trim(),
1372
1375
  type,
1376
+ // Sub-task linkage: parentId is not an editable form field — it rides
1377
+ // through from defaultValues (the "Add sub-task" flow) so a created work
1378
+ // item nests under its parent.
1379
+ ...defaultValues?.parentId ? { parentId: defaultValues.parentId } : {},
1373
1380
  ...hidden.has("status") ? {} : { status },
1374
1381
  ...hidden.has("priority") ? {} : { priority },
1375
1382
  ...hidden.has("description") || !description.trim() ? {} : { description: description.trim() },
@@ -2468,6 +2475,8 @@ function TaskDetailCore({
2468
2475
  assigneeOptions,
2469
2476
  linkSourceProduct,
2470
2477
  renderLinkIcon,
2478
+ onOpenItem,
2479
+ onAddSubtask,
2471
2480
  className
2472
2481
  }) {
2473
2482
  const client = useApolloClient();
@@ -2486,6 +2495,20 @@ function TaskDetailCore({
2486
2495
  linkSourceProduct
2487
2496
  );
2488
2497
  const links = item ? linksByItem.get(item.id) ?? [] : [];
2498
+ const { data: parentData } = useQuery(GET_WORK_ITEM, {
2499
+ variables: { id: item?.parentId },
2500
+ skip: !item?.parentId
2501
+ });
2502
+ const parent = parentData?.planmagnetWorkItem ?? null;
2503
+ const { data: childData } = useQuery(LIST_WORK_ITEMS, {
2504
+ variables: {
2505
+ workspaceId: item?.workspaceId,
2506
+ filter: { parentId: item?.id },
2507
+ pagination: { first: 100 }
2508
+ },
2509
+ skip: !item?.id || !item?.workspaceId
2510
+ });
2511
+ const children = childData?.planmagnetWorkItems?.items ?? [];
2489
2512
  const [editing, setEditing] = useState(false);
2490
2513
  const [confirmingDelete, setConfirmingDelete] = useState(false);
2491
2514
  const [title, setTitle] = useState("");
@@ -2681,6 +2704,56 @@ function TaskDetailCore({
2681
2704
  link.id
2682
2705
  )) })
2683
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(
2730
+ "button",
2731
+ {
2732
+ type: "button",
2733
+ onClick: () => onAddSubtask({ id: item.id, projectId: item.projectId }),
2734
+ className: "text-xs font-medium text-primary hover:underline",
2735
+ children: [
2736
+ "+ ",
2737
+ L.addSubtask
2738
+ ]
2739
+ }
2740
+ )
2741
+ ] }),
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
+ ] }),
2684
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: [
2685
2758
  item.createdAt && /* @__PURE__ */ jsxs("span", { children: [
2686
2759
  L.createdLabel,
@@ -2924,6 +2997,7 @@ function TasksPageCore({
2924
2997
  const [mine, setMine] = useState(false);
2925
2998
  const [creating, setCreating] = useState(false);
2926
2999
  const [selectedId, setSelectedId] = useState(null);
3000
+ const [createDefaults, setCreateDefaults] = useState(null);
2927
3001
  useEffect(() => {
2928
3002
  if (openItemId) setSelectedId(openItemId);
2929
3003
  }, [openItemId]);
@@ -2935,10 +3009,31 @@ function TasksPageCore({
2935
3009
  (workItem) => {
2936
3010
  refetchBoard();
2937
3011
  setCreating(false);
3012
+ setCreateDefaults(null);
2938
3013
  onTaskSaved?.({ workItem, operation: "created" });
2939
3014
  },
2940
3015
  [refetchBoard, onTaskSaved]
2941
3016
  );
3017
+ const handleAddSubtask = useCallback(
3018
+ (parent) => {
3019
+ setSelectedId(null);
3020
+ setCreateDefaults({
3021
+ parentId: parent.id,
3022
+ projectId: parent.projectId,
3023
+ type: "SUB_TASK"
3024
+ });
3025
+ setCreating(true);
3026
+ },
3027
+ []
3028
+ );
3029
+ const startCreate = useCallback(() => {
3030
+ setCreateDefaults(null);
3031
+ setCreating(true);
3032
+ }, []);
3033
+ const cancelCreate = useCallback(() => {
3034
+ setCreating(false);
3035
+ setCreateDefaults(null);
3036
+ }, []);
2942
3037
  const handleUpdated = useCallback(
2943
3038
  (workItem) => {
2944
3039
  refetchBoard();
@@ -2966,8 +3061,9 @@ function TasksPageCore({
2966
3061
  projectId,
2967
3062
  assigneeOptions,
2968
3063
  hiddenFields,
3064
+ defaultValues: createDefaults ?? void 0,
2969
3065
  onCreated: handleCreated,
2970
- onCancel: () => setCreating(false)
3066
+ onCancel: cancelCreate
2971
3067
  }
2972
3068
  );
2973
3069
  const detailView = selectedId != null ? /* @__PURE__ */ jsx(
@@ -2981,7 +3077,9 @@ function TasksPageCore({
2981
3077
  linkSourceProduct,
2982
3078
  renderLinkIcon,
2983
3079
  onUpdated: handleUpdated,
2984
- onDeleted: handleDeleted
3080
+ onDeleted: handleDeleted,
3081
+ onOpenItem: handleSelect,
3082
+ onAddSubtask: handleAddSubtask
2985
3083
  }
2986
3084
  ) : null;
2987
3085
  const listPriority = priority === "ALL" ? void 0 : priority;
@@ -3009,7 +3107,7 @@ function TasksPageCore({
3009
3107
  onPriorityChange: setPriority,
3010
3108
  type,
3011
3109
  onTypeChange: setType,
3012
- onNewTask: () => setCreating(true),
3110
+ onNewTask: startCreate,
3013
3111
  canCreate,
3014
3112
  mine,
3015
3113
  onMineChange: currentActorId ? setMine : void 0
@@ -3071,7 +3169,7 @@ function TasksPageCore({
3071
3169
  Modal,
3072
3170
  {
3073
3171
  open: creating,
3074
- onClose: () => setCreating(false),
3172
+ onClose: cancelCreate,
3075
3173
  ariaLabel: "Create task",
3076
3174
  className: "max-w-xl",
3077
3175
  children: /* @__PURE__ */ jsx("div", { className: "p-2 pt-8", children: createForm })