@burdenoff/planmagnet-fe-sdk 2026.703.2 → 2026.703.3

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,12 +1,12 @@
1
1
  import { createContext, useMemo, useEffect, useContext, useState, useCallback } from 'react';
2
- import { ApolloProvider, useApolloClient, useMutation, useQuery } from '@apollo/client/react';
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';
5
5
  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, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, CalendarRange, List, LayoutGrid, Search, Plus } from 'lucide-react';
9
+ import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, CalendarRange, List, LayoutGrid, Search, Plus } 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';
@@ -410,6 +410,63 @@ var MOVE_WORK_ITEM = gql`
410
410
  }
411
411
  ${WORK_ITEM_FIELDS}
412
412
  `;
413
+ var WORK_ITEM_LINK_FIELDS = gql`
414
+ fragment WorkItemLinkFields on WorkItemLink {
415
+ id
416
+ workspaceId
417
+ workItemId
418
+ sourceProduct
419
+ entityType
420
+ entityId
421
+ relationType
422
+ displayNameSnapshot
423
+ deepLinkSnapshot
424
+ metadata
425
+ createdBy
426
+ onBehalfOf
427
+ createdAt
428
+ updatedAt
429
+ }
430
+ `;
431
+ var LIST_WORK_ITEM_LINKS = gql`
432
+ query ListWorkItemLinks(
433
+ $workspaceId: ID!
434
+ $filter: PlanmagnetWorkItemLinkFilter
435
+ $pagination: PlanmagnetPaginationInput
436
+ ) {
437
+ planmagnetWorkItemLinks(
438
+ workspaceId: $workspaceId
439
+ filter: $filter
440
+ pagination: $pagination
441
+ ) {
442
+ items {
443
+ ...WorkItemLinkFields
444
+ }
445
+ pageInfo {
446
+ hasNextPage
447
+ hasPreviousPage
448
+ startCursor
449
+ endCursor
450
+ totalCount
451
+ }
452
+ totalCount
453
+ }
454
+ }
455
+ ${WORK_ITEM_LINK_FIELDS}
456
+ `;
457
+ var CREATE_WORK_ITEM_LINK = gql`
458
+ mutation CreateWorkItemLink($input: PlanmagnetCreateWorkItemLinkInput!) {
459
+ planmagnetLinkWorkItem(input: $input) {
460
+ ...WorkItemLinkFields
461
+ }
462
+ }
463
+ ${WORK_ITEM_LINK_FIELDS}
464
+ `;
465
+ var DELETE_WORK_ITEM_LINK = gql`
466
+ mutation UnlinkWorkItem($id: ID!) {
467
+ planmagnetUnlinkWorkItem(id: $id)
468
+ }
469
+ `;
413
470
  function cn(...inputs) {
414
471
  return twMerge(clsx(inputs));
415
472
  }
@@ -552,6 +609,36 @@ var TASK_STATUSES = [
552
609
  function enumLabel(value) {
553
610
  return value.toLowerCase().replace(/_/g, " ");
554
611
  }
612
+ function LinkChip({ link, renderIcon, className }) {
613
+ const label = link.displayNameSnapshot || enumLabel(link.entityType);
614
+ const icon = renderIcon?.(link.entityType) ?? /* @__PURE__ */ jsx(Link2, { className: "h-3 w-3 shrink-0", "aria-hidden": "true" });
615
+ const body = /* @__PURE__ */ jsxs(
616
+ "span",
617
+ {
618
+ className: cn(
619
+ "inline-flex max-w-full items-center gap-1 rounded-full border border-primary/20 bg-primary/5 px-1.5 py-0.5 text-[10px] font-medium text-primary",
620
+ className
621
+ ),
622
+ title: `${enumLabel(link.relationType)} \xB7 ${enumLabel(link.entityType)}`,
623
+ children: [
624
+ icon,
625
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: label })
626
+ ]
627
+ }
628
+ );
629
+ if (link.deepLinkSnapshot) {
630
+ return /* @__PURE__ */ jsx(
631
+ "a",
632
+ {
633
+ href: link.deepLinkSnapshot,
634
+ onClick: (e) => e.stopPropagation(),
635
+ className: "inline-flex max-w-full no-underline hover:brightness-95",
636
+ children: body
637
+ }
638
+ );
639
+ }
640
+ return body;
641
+ }
555
642
  var STATUS_META = {
556
643
  BACKLOG: { icon: CircleDashed, tone: "text-muted-foreground/60" },
557
644
  TODO: { icon: Circle, tone: "text-muted-foreground" },
@@ -705,12 +792,15 @@ function TaskCard({
705
792
  onDragEnd,
706
793
  dragging,
707
794
  resolveAssignee,
795
+ links,
796
+ renderLinkIcon,
708
797
  className
709
798
  }) {
710
799
  const overdue = isOverdue(task.dueDate, task.status);
711
800
  const due = formatDueDate(task.dueDate);
712
801
  const resolved = task.assigneeId ? resolveAssignee?.(task.assigneeId) : null;
713
802
  const showTopRow = !hidden.has("key") || !hidden.has("type");
803
+ const showLinks = !hidden.has("links") && (links?.length ?? 0) > 0;
714
804
  const showLabels = !hidden.has("labels") && (task.labels?.length ?? 0) > 0;
715
805
  const showPriority = !hidden.has("priority");
716
806
  const showPoints = !hidden.has("storyPoints") && task.storyPoints != null;
@@ -749,6 +839,13 @@ function TaskCard({
749
839
  !hidden.has("type") && /* @__PURE__ */ jsx(TypeBadge, { type: task.type })
750
840
  ] }),
751
841
  /* @__PURE__ */ jsx("p", { className: "line-clamp-2 text-sm font-medium leading-snug text-foreground", children: task.title }),
842
+ showLinks && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
843
+ links.slice(0, 3).map((link) => /* @__PURE__ */ jsx(LinkChip, { link, renderIcon: renderLinkIcon }, link.id)),
844
+ links.length > 3 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
845
+ "+",
846
+ links.length - 3
847
+ ] })
848
+ ] }),
752
849
  showLabels && /* @__PURE__ */ jsxs("div", { className: "mt-1.5 flex flex-wrap gap-1", children: [
753
850
  task.labels.slice(0, 3).map((l) => /* @__PURE__ */ jsx(
754
851
  "span",
@@ -798,6 +895,29 @@ function TaskCard({
798
895
  }
799
896
  );
800
897
  }
898
+ function useWorkItemLinks(workspaceId, sourceProduct, limit = 500) {
899
+ const { data, loading } = useQuery(
900
+ LIST_WORK_ITEM_LINKS,
901
+ {
902
+ variables: {
903
+ workspaceId,
904
+ ...sourceProduct ? { filter: { sourceProduct } } : {},
905
+ pagination: { first: limit }
906
+ },
907
+ skip: !workspaceId || !sourceProduct
908
+ }
909
+ );
910
+ const linksByItem = useMemo(() => {
911
+ const map = /* @__PURE__ */ new Map();
912
+ for (const link of data?.planmagnetWorkItemLinks?.items ?? []) {
913
+ const arr = map.get(link.workItemId);
914
+ if (arr) arr.push(link);
915
+ else map.set(link.workItemId, [link]);
916
+ }
917
+ return map;
918
+ }, [data]);
919
+ return { linksByItem, loading };
920
+ }
801
921
  function TaskBoardCore({
802
922
  projectId,
803
923
  columns = DEFAULT_BOARD_COLUMNS,
@@ -809,6 +929,8 @@ function TaskBoardCore({
809
929
  assigneeId,
810
930
  hiddenFields,
811
931
  resolveAssignee,
932
+ linkSourceProduct,
933
+ renderLinkIcon,
812
934
  onTaskClick,
813
935
  className,
814
936
  workspaceId: workspaceIdProp
@@ -817,6 +939,7 @@ function TaskBoardCore({
817
939
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
818
940
  const client = useApolloClient();
819
941
  const hidden = useMemo(() => new Set(hiddenFields ?? []), [hiddenFields]);
942
+ const { linksByItem } = useWorkItemLinks(workspaceId, linkSourceProduct);
820
943
  const [draggingId, setDraggingId] = useState(null);
821
944
  const [dragOver, setDragOver] = useState(null);
822
945
  const [moveError, setMoveError] = useState(null);
@@ -927,6 +1050,8 @@ function TaskBoardCore({
927
1050
  draggable: canDrag,
928
1051
  dragging: draggingId === task.id,
929
1052
  resolveAssignee,
1053
+ links: linksByItem.get(task.id),
1054
+ renderLinkIcon,
930
1055
  onDragStart: (t, e) => {
931
1056
  setDraggingId(t.id);
932
1057
  e.dataTransfer.effectAllowed = "move";
@@ -1292,6 +1417,7 @@ var COLUMN_LABELS = {
1292
1417
  assignee: "Assignee",
1293
1418
  dueDate: "Due Date",
1294
1419
  storyPoints: "Points",
1420
+ linkedRecord: "Linked Record",
1295
1421
  labels: "Labels"
1296
1422
  };
1297
1423
  var COLUMN_SORT = {
@@ -1315,12 +1441,15 @@ function TaskListCore({
1315
1441
  hideHeader,
1316
1442
  onSelect,
1317
1443
  resolveAssignee,
1444
+ linkSourceProduct,
1445
+ renderLinkIcon,
1318
1446
  limit = 200,
1319
1447
  className,
1320
1448
  workspaceId: workspaceIdProp
1321
1449
  }) {
1322
1450
  const ctx = usePlanMagnetContextSafe();
1323
1451
  const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
1452
+ const { linksByItem } = useWorkItemLinks(workspaceId, linkSourceProduct);
1324
1453
  const [sort, setSort] = useState({
1325
1454
  by: sortBy ?? null,
1326
1455
  dir: sortDir
@@ -1422,7 +1551,11 @@ function TaskListCore({
1422
1551
  "td",
1423
1552
  {
1424
1553
  className: "px-3 py-2 align-middle text-foreground",
1425
- children: renderCell(col, task, resolveAssignee)
1554
+ children: renderCell(col, task, {
1555
+ resolveAssignee,
1556
+ links: linksByItem.get(task.id),
1557
+ renderLinkIcon
1558
+ })
1426
1559
  },
1427
1560
  col
1428
1561
  ))
@@ -1433,7 +1566,8 @@ function TaskListCore({
1433
1566
  ] }) })
1434
1567
  ] });
1435
1568
  }
1436
- function renderCell(col, task, resolveAssignee) {
1569
+ function renderCell(col, task, opts) {
1570
+ const { resolveAssignee, links, renderLinkIcon } = opts;
1437
1571
  switch (col) {
1438
1572
  case "key":
1439
1573
  return /* @__PURE__ */ jsx("span", { className: "font-mono text-xs text-muted-foreground", children: task.key });
@@ -1471,6 +1605,17 @@ function renderCell(col, task, resolveAssignee) {
1471
1605
  }
1472
1606
  case "storyPoints":
1473
1607
  return task.storyPoints != null ? /* @__PURE__ */ jsx("span", { className: "flex h-5 min-w-5 items-center justify-center rounded-full bg-primary/10 px-1 text-[10px] font-semibold text-primary", children: task.storyPoints }) : /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
1608
+ case "linkedRecord":
1609
+ if (!links || links.length === 0) {
1610
+ return /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "\u2014" });
1611
+ }
1612
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-1", children: [
1613
+ links.slice(0, 2).map((link) => /* @__PURE__ */ jsx(LinkChip, { link, renderIcon: renderLinkIcon }, link.id)),
1614
+ links.length > 2 && /* @__PURE__ */ jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
1615
+ "+",
1616
+ links.length - 2
1617
+ ] })
1618
+ ] });
1474
1619
  case "labels":
1475
1620
  return /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: (task.labels ?? []).slice(0, 3).map((l) => /* @__PURE__ */ jsx(
1476
1621
  "span",
@@ -2307,6 +2452,8 @@ function TasksPageCore({
2307
2452
  views,
2308
2453
  defaultView = "board",
2309
2454
  resolveAssignee,
2455
+ linkSourceProduct,
2456
+ renderLinkIcon,
2310
2457
  header,
2311
2458
  onTaskSaved,
2312
2459
  createSurface = "modal",
@@ -2372,6 +2519,14 @@ function TasksPageCore({
2372
2519
  ) : null;
2373
2520
  const listPriority = priority === "ALL" ? void 0 : priority;
2374
2521
  const listType = type === "ALL" ? void 0 : type;
2522
+ const listColumns = linkSourceProduct ? [
2523
+ "title",
2524
+ "status",
2525
+ "priority",
2526
+ "assignee",
2527
+ "dueDate",
2528
+ "linkedRecord"
2529
+ ] : void 0;
2375
2530
  return /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
2376
2531
  header && /* @__PURE__ */ jsx("div", { className: "min-w-0", children: typeof header === "string" ? /* @__PURE__ */ jsx("h1", { className: "truncate text-2xl font-semibold text-foreground", children: header }) : header }),
2377
2532
  /* @__PURE__ */ jsx(
@@ -2404,6 +2559,8 @@ function TasksPageCore({
2404
2559
  search,
2405
2560
  hiddenFields,
2406
2561
  resolveAssignee,
2562
+ linkSourceProduct,
2563
+ renderLinkIcon,
2407
2564
  onTaskClick: handleTaskClick
2408
2565
  }
2409
2566
  ),
@@ -2417,6 +2574,9 @@ function TasksPageCore({
2417
2574
  search: search || void 0,
2418
2575
  hideHeader: true,
2419
2576
  resolveAssignee,
2577
+ linkSourceProduct,
2578
+ renderLinkIcon,
2579
+ columns: listColumns ? [...listColumns] : void 0,
2420
2580
  onSelect: handleSelect
2421
2581
  }
2422
2582
  ),
@@ -2483,6 +2643,6 @@ function TasksPage({
2483
2643
  );
2484
2644
  }
2485
2645
 
2486
- export { Avatar, Badge, CREATE_WORK_ITEM, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_WORK_ITEMS, 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, TaskList, TaskListCore, TaskTimelineCore, TaskToolbar, TasksPage, TasksPageCore, TypeBadge, UPDATE_WORK_ITEM, WORK_ITEM_FIELDS, WORK_ITEM_SUMMARY_FIELDS, badgeVariants, cn, config, createPlanMagnetClient, enumLabel, getGraphQLEndpoint, getPlanMagnetClient, getPlanMagnetClientSafe, getPlanMagnetConfig, isPlanMagnetClientReady, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, usePlanMagnetContext, usePlanMagnetContextSafe, validateTrustedOrigin, waitForPlanMagnetClient };
2646
+ export { Avatar, Badge, CREATE_WORK_ITEM, CREATE_WORK_ITEM_LINK, CreateTask, CreateTaskCore, DEFAULT_BOARD_COLUMNS, DELETE_WORK_ITEM_LINK, Drawer, GET_PROJECTS, GET_WORK_ITEM, LIST_WORK_ITEMS, LIST_WORK_ITEM_LINKS, LinkChip, 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, 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, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, usePlanMagnetContext, usePlanMagnetContextSafe, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
2487
2647
  //# sourceMappingURL=index.js.map
2488
2648
  //# sourceMappingURL=index.js.map