@burdenoff/planmagnet-fe-sdk 2026.706.7 → 2026.708.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 +177 -5
- package/dist/index.js +680 -43
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
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, Trash2, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, Plus, CalendarRange, List, LayoutGrid, Search, Pencil, SmilePlus } from 'lucide-react';
|
|
9
|
+
import { CircleX, CircleCheckBig, CircleDotDashed, CircleDot, Circle, CircleDashed, User, Link2, CalendarDays, Trash2, ChevronUp, ChevronDown, ChevronLeft, ChevronRight, Flag, Plus, CalendarRange, List, LayoutGrid, Search, Bookmark, Check, Pencil, SmilePlus } from 'lucide-react';
|
|
10
10
|
import { clsx } from 'clsx';
|
|
11
11
|
import { twMerge } from 'tailwind-merge';
|
|
12
12
|
import { startOfWeek, format, addDays, addWeeks, endOfWeek, eachDayOfInterval, addMonths, startOfMonth, endOfMonth, isToday, isSameMonth, startOfDay, parseISO, isValid } from 'date-fns';
|
|
@@ -282,6 +282,12 @@ var WORK_ITEM_SUMMARY_FIELDS = gql`
|
|
|
282
282
|
storyPoints
|
|
283
283
|
dueDate
|
|
284
284
|
labels
|
|
285
|
+
# Board reorder ordering. sortOrder maps a Prisma \`position\` column and is
|
|
286
|
+
# unsafe to SELECT BACK FROM A MUTATION (serialization crash), but SAFE on
|
|
287
|
+
# this QUERY read path — a WorkItem.sortOrder field resolver maps position →
|
|
288
|
+
# sortOrder. The board sorts cards by this ascending so a drag-to-reorder
|
|
289
|
+
# persists across refetch (the list query itself orders by createdAt).
|
|
290
|
+
sortOrder
|
|
285
291
|
}
|
|
286
292
|
`;
|
|
287
293
|
var WORK_ITEM_FIELDS = gql`
|
|
@@ -406,10 +412,11 @@ var UPDATE_WORK_ITEM = gql`
|
|
|
406
412
|
var MOVE_WORK_ITEM = gql`
|
|
407
413
|
mutation MoveWorkItem($id: ID!, $input: PlanmagnetMoveWorkItemInput!) {
|
|
408
414
|
planmagnetMoveWorkItem(id: $id, input: $input) {
|
|
409
|
-
|
|
415
|
+
id
|
|
416
|
+
status
|
|
417
|
+
sprintId
|
|
410
418
|
}
|
|
411
419
|
}
|
|
412
|
-
${WORK_ITEM_FIELDS}
|
|
413
420
|
`;
|
|
414
421
|
var WORK_ITEM_LINK_FIELDS = gql`
|
|
415
422
|
fragment WorkItemLinkFields on WorkItemLink {
|
|
@@ -808,6 +815,22 @@ var DEFAULT_TASK_LABELS = {
|
|
|
808
815
|
allTypes: "All types",
|
|
809
816
|
newTask: "New task",
|
|
810
817
|
myTasks: "My tasks",
|
|
818
|
+
savedViews: "Views",
|
|
819
|
+
saveView: "Save current view",
|
|
820
|
+
viewName: "View name",
|
|
821
|
+
saveViewAction: "Save",
|
|
822
|
+
deleteView: "Delete",
|
|
823
|
+
noSavedViews: "No saved views",
|
|
824
|
+
defaultViewName: "Default",
|
|
825
|
+
keyboardShortcuts: "Keyboard shortcuts",
|
|
826
|
+
shortcutNewTask: "New task",
|
|
827
|
+
shortcutSearch: "Focus search",
|
|
828
|
+
shortcutBoardView: "Board view",
|
|
829
|
+
shortcutListView: "List view",
|
|
830
|
+
shortcutTimelineView: "Timeline view",
|
|
831
|
+
shortcutMyTasks: "Toggle My tasks",
|
|
832
|
+
shortcutHelp: "Toggle this help",
|
|
833
|
+
shortcutClose: "Close overlay",
|
|
811
834
|
noTasks: "No tasks",
|
|
812
835
|
noTasksFound: "No tasks found.",
|
|
813
836
|
dropHere: "Drop here",
|
|
@@ -896,6 +919,7 @@ var DEFAULT_TASK_LABELS = {
|
|
|
896
919
|
projectRequired: "A projectId is required to create a task.",
|
|
897
920
|
errorLoading: "Error loading tasks",
|
|
898
921
|
moveFailed: "Couldn't move the task. Please try again.",
|
|
922
|
+
reorderFailed: "Couldn't reorder the task. Please try again.",
|
|
899
923
|
failedToCreate: "Failed to create task",
|
|
900
924
|
failedToSave: "Failed to save",
|
|
901
925
|
failedToDelete: "Failed to delete",
|
|
@@ -1094,6 +1118,7 @@ function TaskCard({
|
|
|
1094
1118
|
draggable,
|
|
1095
1119
|
onDragStart,
|
|
1096
1120
|
onDragEnd,
|
|
1121
|
+
onDragOver,
|
|
1097
1122
|
dragging,
|
|
1098
1123
|
resolveAssignee,
|
|
1099
1124
|
links,
|
|
@@ -1128,6 +1153,7 @@ function TaskCard({
|
|
|
1128
1153
|
draggable,
|
|
1129
1154
|
onDragStart: draggable ? (e) => onDragStart?.(task, e) : void 0,
|
|
1130
1155
|
onDragEnd: draggable ? (e) => onDragEnd?.(task, e) : void 0,
|
|
1156
|
+
onDragOver: draggable ? onDragOver : void 0,
|
|
1131
1157
|
onClick: onClick ? () => onClick(task) : void 0,
|
|
1132
1158
|
onKeyDown: handleKey,
|
|
1133
1159
|
className: cn(
|
|
@@ -1223,6 +1249,13 @@ function useWorkItemLinks(workspaceId, sourceProduct, limit = 500) {
|
|
|
1223
1249
|
}, [data]);
|
|
1224
1250
|
return { linksByItem, loading };
|
|
1225
1251
|
}
|
|
1252
|
+
function bySortOrder(tasks) {
|
|
1253
|
+
return tasks.map((task, index) => ({ task, index })).sort((a, b) => {
|
|
1254
|
+
const ao = a.task.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
1255
|
+
const bo = b.task.sortOrder ?? Number.MAX_SAFE_INTEGER;
|
|
1256
|
+
return ao === bo ? a.index - b.index : ao - bo;
|
|
1257
|
+
}).map((entry) => entry.task);
|
|
1258
|
+
}
|
|
1226
1259
|
function TaskBoardCore({
|
|
1227
1260
|
projectId,
|
|
1228
1261
|
columns = DEFAULT_BOARD_COLUMNS,
|
|
@@ -1249,9 +1282,11 @@ function TaskBoardCore({
|
|
|
1249
1282
|
const { linksByItem } = useWorkItemLinks(workspaceId, linkSourceProduct);
|
|
1250
1283
|
const [draggingId, setDraggingId] = useState(null);
|
|
1251
1284
|
const [dragOver, setDragOver] = useState(null);
|
|
1285
|
+
const [dropIndex, setDropIndex] = useState(null);
|
|
1252
1286
|
const [moveError, setMoveError] = useState(null);
|
|
1253
1287
|
const [pendingMove, setPendingMove] = useState(null);
|
|
1254
1288
|
const [updateItem] = useMutation(UPDATE_WORK_ITEM);
|
|
1289
|
+
const [moveItem] = useMutation(MOVE_WORK_ITEM);
|
|
1255
1290
|
const { data, loading, error } = useQuery(
|
|
1256
1291
|
LIST_WORK_ITEMS,
|
|
1257
1292
|
{
|
|
@@ -1280,6 +1315,44 @@ function TaskBoardCore({
|
|
|
1280
1315
|
},
|
|
1281
1316
|
[client, updateItem, L]
|
|
1282
1317
|
);
|
|
1318
|
+
const reorderTask = useCallback(
|
|
1319
|
+
(columnTasks, fromIndex, toIndex) => {
|
|
1320
|
+
if (fromIndex === toIndex || fromIndex < 0) return;
|
|
1321
|
+
const ordered = columnTasks.slice();
|
|
1322
|
+
const [moved] = ordered.splice(fromIndex, 1);
|
|
1323
|
+
if (!moved) return;
|
|
1324
|
+
const insertAt = Math.max(0, Math.min(toIndex, ordered.length));
|
|
1325
|
+
ordered.splice(insertAt, 0, moved);
|
|
1326
|
+
const changed = [];
|
|
1327
|
+
ordered.forEach((task, index) => {
|
|
1328
|
+
const prev = task.sortOrder ?? null;
|
|
1329
|
+
if (prev !== index) {
|
|
1330
|
+
changed.push({ id: task.id, from: prev, to: index });
|
|
1331
|
+
}
|
|
1332
|
+
});
|
|
1333
|
+
if (changed.length === 0) return;
|
|
1334
|
+
setMoveError(null);
|
|
1335
|
+
const setOrder = (id, value) => {
|
|
1336
|
+
const cacheId = client.cache.identify({ __typename: "WorkItem", id });
|
|
1337
|
+
if (cacheId) {
|
|
1338
|
+
client.cache.modify({
|
|
1339
|
+
id: cacheId,
|
|
1340
|
+
fields: { sortOrder: () => value }
|
|
1341
|
+
});
|
|
1342
|
+
}
|
|
1343
|
+
};
|
|
1344
|
+
for (const c of changed) setOrder(c.id, c.to);
|
|
1345
|
+
Promise.all(
|
|
1346
|
+
changed.map(
|
|
1347
|
+
(c) => moveItem({ variables: { id: c.id, input: { sortOrder: c.to } } })
|
|
1348
|
+
)
|
|
1349
|
+
).catch(() => {
|
|
1350
|
+
for (const c of changed) setOrder(c.id, c.from);
|
|
1351
|
+
setMoveError(L.reorderFailed);
|
|
1352
|
+
});
|
|
1353
|
+
},
|
|
1354
|
+
[client, moveItem, L]
|
|
1355
|
+
);
|
|
1283
1356
|
if (!workspaceId) {
|
|
1284
1357
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TaskBoard: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
1285
1358
|
}
|
|
@@ -1306,18 +1379,41 @@ function TaskBoardCore({
|
|
|
1306
1379
|
return true;
|
|
1307
1380
|
});
|
|
1308
1381
|
const canDrag = !readonly;
|
|
1382
|
+
const columnTasksFor = (status) => bySortOrder(filtered.filter((t) => t.status === status));
|
|
1309
1383
|
const onColumnDragOver = (status) => (e) => {
|
|
1310
1384
|
if (!canDrag || !draggingId) return;
|
|
1311
1385
|
e.preventDefault();
|
|
1312
1386
|
if (dragOver !== status) setDragOver(status);
|
|
1387
|
+
if (dropIndex === null) setDropIndex(columnTasksFor(status).length);
|
|
1388
|
+
};
|
|
1389
|
+
const onCardDragOver = (status, index) => (e) => {
|
|
1390
|
+
if (!canDrag || !draggingId) return;
|
|
1391
|
+
e.preventDefault();
|
|
1392
|
+
e.stopPropagation();
|
|
1393
|
+
if (dragOver !== status) setDragOver(status);
|
|
1394
|
+
const rect = e.currentTarget.getBoundingClientRect();
|
|
1395
|
+
const after = e.clientY - rect.top > rect.height / 2;
|
|
1396
|
+
const next = after ? index + 1 : index;
|
|
1397
|
+
if (dropIndex !== next) setDropIndex(next);
|
|
1398
|
+
};
|
|
1399
|
+
const clearDrag = () => {
|
|
1400
|
+
setDragOver(null);
|
|
1401
|
+
setDraggingId(null);
|
|
1402
|
+
setDropIndex(null);
|
|
1313
1403
|
};
|
|
1314
1404
|
const onColumnDrop = (status) => (e) => {
|
|
1315
1405
|
if (!canDrag) return;
|
|
1316
1406
|
e.preventDefault();
|
|
1317
1407
|
const task = tasks.find((t) => t.id === draggingId);
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
if (!task
|
|
1408
|
+
const targetIndex = dropIndex;
|
|
1409
|
+
clearDrag();
|
|
1410
|
+
if (!task) return;
|
|
1411
|
+
if (task.status === status) {
|
|
1412
|
+
const colTasks = columnTasksFor(status);
|
|
1413
|
+
const fromIndex = colTasks.findIndex((t) => t.id === task.id);
|
|
1414
|
+
if (targetIndex !== null) reorderTask(colTasks, fromIndex, targetIndex);
|
|
1415
|
+
return;
|
|
1416
|
+
}
|
|
1321
1417
|
if (confirmMove) setPendingMove({ task, toStatus: status });
|
|
1322
1418
|
else moveTask(task, status);
|
|
1323
1419
|
};
|
|
@@ -1332,8 +1428,9 @@ function TaskBoardCore({
|
|
|
1332
1428
|
] }),
|
|
1333
1429
|
moveError && /* @__PURE__ */ jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/10 px-3 py-1.5 text-xs text-destructive", children: moveError }),
|
|
1334
1430
|
/* @__PURE__ */ jsx("div", { className: "flex gap-3 overflow-x-auto pb-2", children: columns.map((col) => {
|
|
1335
|
-
const colTasks =
|
|
1431
|
+
const colTasks = columnTasksFor(col.status);
|
|
1336
1432
|
const isOver = dragOver === col.status;
|
|
1433
|
+
const showDropLine = canDrag && isOver && dropIndex !== null && colTasks.some((t) => t.id === draggingId);
|
|
1337
1434
|
return /* @__PURE__ */ jsxs(
|
|
1338
1435
|
"div",
|
|
1339
1436
|
{
|
|
@@ -1352,29 +1449,45 @@ function TaskBoardCore({
|
|
|
1352
1449
|
] }),
|
|
1353
1450
|
/* @__PURE__ */ jsx("span", { className: "rounded-full bg-muted px-1.5 text-xs font-medium text-muted-foreground", children: colTasks.length })
|
|
1354
1451
|
] }),
|
|
1355
|
-
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 px-2 pb-2", children: colTasks.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/70 px-3 py-6 text-center text-xs text-muted-foreground", children: isOver ? L.dropHere : L.noTasks }) : colTasks.map((task) => /* @__PURE__ */
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
dragging: draggingId === task.id,
|
|
1363
|
-
resolveAssignee,
|
|
1364
|
-
links: linksByItem.get(task.id),
|
|
1365
|
-
renderLinkIcon,
|
|
1366
|
-
onDragStart: (t, e) => {
|
|
1367
|
-
setDraggingId(t.id);
|
|
1368
|
-
e.dataTransfer.effectAllowed = "move";
|
|
1369
|
-
e.dataTransfer.setData("text/plain", t.id);
|
|
1370
|
-
},
|
|
1371
|
-
onDragEnd: () => {
|
|
1372
|
-
setDraggingId(null);
|
|
1373
|
-
setDragOver(null);
|
|
1452
|
+
/* @__PURE__ */ jsx("div", { className: "flex flex-col gap-2 px-2 pb-2", children: colTasks.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-md border border-dashed border-border/70 px-3 py-6 text-center text-xs text-muted-foreground", children: isOver ? L.dropHere : L.noTasks }) : colTasks.map((task, index) => /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
|
|
1453
|
+
showDropLine && dropIndex === index && /* @__PURE__ */ jsx(
|
|
1454
|
+
"div",
|
|
1455
|
+
{
|
|
1456
|
+
"aria-hidden": "true",
|
|
1457
|
+
"data-testid": "reorder-drop-line",
|
|
1458
|
+
className: "h-0.5 rounded-full bg-primary"
|
|
1374
1459
|
}
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1460
|
+
),
|
|
1461
|
+
/* @__PURE__ */ jsx(
|
|
1462
|
+
TaskCard,
|
|
1463
|
+
{
|
|
1464
|
+
task,
|
|
1465
|
+
hidden,
|
|
1466
|
+
onClick: onTaskClick,
|
|
1467
|
+
draggable: canDrag,
|
|
1468
|
+
dragging: draggingId === task.id,
|
|
1469
|
+
resolveAssignee,
|
|
1470
|
+
links: linksByItem.get(task.id),
|
|
1471
|
+
renderLinkIcon,
|
|
1472
|
+
onDragOver: onCardDragOver(col.status, index),
|
|
1473
|
+
onDragStart: (t, e) => {
|
|
1474
|
+
setDraggingId(t.id);
|
|
1475
|
+
setDropIndex(null);
|
|
1476
|
+
e.dataTransfer.effectAllowed = "move";
|
|
1477
|
+
e.dataTransfer.setData("text/plain", t.id);
|
|
1478
|
+
},
|
|
1479
|
+
onDragEnd: clearDrag
|
|
1480
|
+
}
|
|
1481
|
+
),
|
|
1482
|
+
showDropLine && index === colTasks.length - 1 && dropIndex >= colTasks.length && /* @__PURE__ */ jsx(
|
|
1483
|
+
"div",
|
|
1484
|
+
{
|
|
1485
|
+
"aria-hidden": "true",
|
|
1486
|
+
"data-testid": "reorder-drop-line",
|
|
1487
|
+
className: "h-0.5 rounded-full bg-primary"
|
|
1488
|
+
}
|
|
1489
|
+
)
|
|
1490
|
+
] }, task.id)) })
|
|
1378
1491
|
]
|
|
1379
1492
|
},
|
|
1380
1493
|
col.status
|
|
@@ -2796,6 +2909,7 @@ function TaskToolbar({
|
|
|
2796
2909
|
views = ["board", "list", "timeline"],
|
|
2797
2910
|
search,
|
|
2798
2911
|
onSearchChange,
|
|
2912
|
+
searchInputRef,
|
|
2799
2913
|
priority,
|
|
2800
2914
|
onPriorityChange,
|
|
2801
2915
|
type,
|
|
@@ -2804,6 +2918,7 @@ function TaskToolbar({
|
|
|
2804
2918
|
canCreate,
|
|
2805
2919
|
mine,
|
|
2806
2920
|
onMineChange,
|
|
2921
|
+
leadingActions,
|
|
2807
2922
|
className
|
|
2808
2923
|
}) {
|
|
2809
2924
|
const L = useTaskLabels();
|
|
@@ -2852,6 +2967,7 @@ function TaskToolbar({
|
|
|
2852
2967
|
}
|
|
2853
2968
|
),
|
|
2854
2969
|
/* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
2970
|
+
leadingActions,
|
|
2855
2971
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2856
2972
|
/* @__PURE__ */ jsx(
|
|
2857
2973
|
Search,
|
|
@@ -2863,6 +2979,7 @@ function TaskToolbar({
|
|
|
2863
2979
|
/* @__PURE__ */ jsx(
|
|
2864
2980
|
"input",
|
|
2865
2981
|
{
|
|
2982
|
+
ref: searchInputRef,
|
|
2866
2983
|
type: "search",
|
|
2867
2984
|
value: search,
|
|
2868
2985
|
onChange: (e) => onSearchChange(e.target.value),
|
|
@@ -3921,6 +4038,300 @@ function TaskDetail({
|
|
|
3921
4038
|
}
|
|
3922
4039
|
);
|
|
3923
4040
|
}
|
|
4041
|
+
function SavedViewsMenu({
|
|
4042
|
+
views,
|
|
4043
|
+
activeViewId,
|
|
4044
|
+
onSaveView,
|
|
4045
|
+
onApplyView,
|
|
4046
|
+
onDeleteView,
|
|
4047
|
+
className
|
|
4048
|
+
}) {
|
|
4049
|
+
const L = useTaskLabels();
|
|
4050
|
+
const [open, setOpen] = useState(false);
|
|
4051
|
+
const [naming, setNaming] = useState(false);
|
|
4052
|
+
const [name, setName] = useState("");
|
|
4053
|
+
const rootRef = useRef(null);
|
|
4054
|
+
useEffect(() => {
|
|
4055
|
+
if (!open) return;
|
|
4056
|
+
const onDown = (e) => {
|
|
4057
|
+
if (rootRef.current && !rootRef.current.contains(e.target)) {
|
|
4058
|
+
setOpen(false);
|
|
4059
|
+
setNaming(false);
|
|
4060
|
+
}
|
|
4061
|
+
};
|
|
4062
|
+
const onKey = (e) => {
|
|
4063
|
+
if (e.key === "Escape") {
|
|
4064
|
+
setOpen(false);
|
|
4065
|
+
setNaming(false);
|
|
4066
|
+
}
|
|
4067
|
+
};
|
|
4068
|
+
document.addEventListener("mousedown", onDown);
|
|
4069
|
+
document.addEventListener("keydown", onKey);
|
|
4070
|
+
return () => {
|
|
4071
|
+
document.removeEventListener("mousedown", onDown);
|
|
4072
|
+
document.removeEventListener("keydown", onKey);
|
|
4073
|
+
};
|
|
4074
|
+
}, [open]);
|
|
4075
|
+
const activeName = views.find((v) => v.id === activeViewId)?.name ?? L.defaultViewName;
|
|
4076
|
+
const commitSave = () => {
|
|
4077
|
+
const trimmed = name.trim();
|
|
4078
|
+
if (!trimmed) return;
|
|
4079
|
+
onSaveView(trimmed);
|
|
4080
|
+
setName("");
|
|
4081
|
+
setNaming(false);
|
|
4082
|
+
setOpen(false);
|
|
4083
|
+
};
|
|
4084
|
+
return /* @__PURE__ */ jsxs("div", { ref: rootRef, className: cn("relative", className), children: [
|
|
4085
|
+
/* @__PURE__ */ jsxs(
|
|
4086
|
+
"button",
|
|
4087
|
+
{
|
|
4088
|
+
type: "button",
|
|
4089
|
+
onClick: () => setOpen((o) => !o),
|
|
4090
|
+
"aria-haspopup": "menu",
|
|
4091
|
+
"aria-expanded": open,
|
|
4092
|
+
"aria-label": L.savedViews,
|
|
4093
|
+
className: cn(
|
|
4094
|
+
"flex h-8 max-w-[10rem] items-center gap-1.5 rounded-md border px-2.5 text-xs font-medium transition-colors",
|
|
4095
|
+
activeViewId ? "border-primary bg-primary/10 text-primary" : "border-border bg-background text-muted-foreground hover:bg-muted/50 hover:text-foreground"
|
|
4096
|
+
),
|
|
4097
|
+
children: [
|
|
4098
|
+
/* @__PURE__ */ jsx(Bookmark, { className: "h-3.5 w-3.5 shrink-0", "aria-hidden": "true" }),
|
|
4099
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: activeName })
|
|
4100
|
+
]
|
|
4101
|
+
}
|
|
4102
|
+
),
|
|
4103
|
+
open && /* @__PURE__ */ jsxs(
|
|
4104
|
+
"div",
|
|
4105
|
+
{
|
|
4106
|
+
role: "menu",
|
|
4107
|
+
className: "absolute right-0 top-full z-30 mt-1 w-56 rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md",
|
|
4108
|
+
children: [
|
|
4109
|
+
/* @__PURE__ */ jsx("div", { className: "px-2 py-1 text-[0.65rem] font-semibold uppercase tracking-wide text-muted-foreground", children: L.savedViews }),
|
|
4110
|
+
views.length === 0 ? /* @__PURE__ */ jsx("div", { className: "px-2 py-1.5 text-xs text-muted-foreground", children: L.noSavedViews }) : /* @__PURE__ */ jsx("ul", { className: "max-h-56 overflow-auto", children: views.map((v) => {
|
|
4111
|
+
const active = v.id === activeViewId;
|
|
4112
|
+
return /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-1", children: [
|
|
4113
|
+
/* @__PURE__ */ jsxs(
|
|
4114
|
+
"button",
|
|
4115
|
+
{
|
|
4116
|
+
type: "button",
|
|
4117
|
+
role: "menuitemradio",
|
|
4118
|
+
"aria-checked": active,
|
|
4119
|
+
onClick: () => {
|
|
4120
|
+
onApplyView(v.id);
|
|
4121
|
+
setOpen(false);
|
|
4122
|
+
},
|
|
4123
|
+
className: cn(
|
|
4124
|
+
"flex min-w-0 flex-1 items-center gap-1.5 rounded px-2 py-1.5 text-left text-xs transition-colors hover:bg-muted/60",
|
|
4125
|
+
active ? "text-foreground" : "text-muted-foreground"
|
|
4126
|
+
),
|
|
4127
|
+
children: [
|
|
4128
|
+
/* @__PURE__ */ jsx(
|
|
4129
|
+
Check,
|
|
4130
|
+
{
|
|
4131
|
+
className: cn(
|
|
4132
|
+
"h-3.5 w-3.5 shrink-0",
|
|
4133
|
+
active ? "opacity-100" : "opacity-0"
|
|
4134
|
+
),
|
|
4135
|
+
"aria-hidden": "true"
|
|
4136
|
+
}
|
|
4137
|
+
),
|
|
4138
|
+
/* @__PURE__ */ jsx("span", { className: "truncate", children: v.name })
|
|
4139
|
+
]
|
|
4140
|
+
}
|
|
4141
|
+
),
|
|
4142
|
+
/* @__PURE__ */ jsx(
|
|
4143
|
+
"button",
|
|
4144
|
+
{
|
|
4145
|
+
type: "button",
|
|
4146
|
+
onClick: () => onDeleteView(v.id),
|
|
4147
|
+
"aria-label": `${L.deleteView}: ${v.name}`,
|
|
4148
|
+
className: "shrink-0 rounded p-1 text-muted-foreground transition-colors hover:bg-destructive/10 hover:text-destructive",
|
|
4149
|
+
children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
|
|
4150
|
+
}
|
|
4151
|
+
)
|
|
4152
|
+
] }, v.id);
|
|
4153
|
+
}) }),
|
|
4154
|
+
/* @__PURE__ */ jsx("div", { className: "my-1 h-px bg-border" }),
|
|
4155
|
+
naming ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 p-1", children: [
|
|
4156
|
+
/* @__PURE__ */ jsx(
|
|
4157
|
+
"input",
|
|
4158
|
+
{
|
|
4159
|
+
type: "text",
|
|
4160
|
+
autoFocus: true,
|
|
4161
|
+
value: name,
|
|
4162
|
+
onChange: (e) => setName(e.target.value),
|
|
4163
|
+
onKeyDown: (e) => {
|
|
4164
|
+
if (e.key === "Enter") commitSave();
|
|
4165
|
+
},
|
|
4166
|
+
placeholder: L.viewName,
|
|
4167
|
+
"aria-label": L.viewName,
|
|
4168
|
+
className: "h-7 min-w-0 flex-1 rounded border border-border bg-background px-2 text-xs text-foreground placeholder:text-muted-foreground focus:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
4169
|
+
}
|
|
4170
|
+
),
|
|
4171
|
+
/* @__PURE__ */ jsx(
|
|
4172
|
+
"button",
|
|
4173
|
+
{
|
|
4174
|
+
type: "button",
|
|
4175
|
+
onClick: commitSave,
|
|
4176
|
+
disabled: !name.trim(),
|
|
4177
|
+
className: "h-7 shrink-0 rounded bg-primary px-2 text-xs font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-50",
|
|
4178
|
+
children: L.saveViewAction
|
|
4179
|
+
}
|
|
4180
|
+
)
|
|
4181
|
+
] }) : /* @__PURE__ */ jsxs(
|
|
4182
|
+
"button",
|
|
4183
|
+
{
|
|
4184
|
+
type: "button",
|
|
4185
|
+
role: "menuitem",
|
|
4186
|
+
onClick: () => setNaming(true),
|
|
4187
|
+
className: "flex w-full items-center gap-1.5 rounded px-2 py-1.5 text-left text-xs font-medium text-foreground transition-colors hover:bg-muted/60",
|
|
4188
|
+
children: [
|
|
4189
|
+
/* @__PURE__ */ jsx(Plus, { className: "h-3.5 w-3.5", "aria-hidden": "true" }),
|
|
4190
|
+
L.saveView
|
|
4191
|
+
]
|
|
4192
|
+
}
|
|
4193
|
+
)
|
|
4194
|
+
]
|
|
4195
|
+
}
|
|
4196
|
+
)
|
|
4197
|
+
] });
|
|
4198
|
+
}
|
|
4199
|
+
var STORAGE_PREFIX = "planmagnet:views:";
|
|
4200
|
+
var DEBOUNCE_MS = 400;
|
|
4201
|
+
function makeStorageKey(workspaceId, suffix) {
|
|
4202
|
+
return suffix ? `${STORAGE_PREFIX}${workspaceId}:${suffix}` : `${STORAGE_PREFIX}${workspaceId}`;
|
|
4203
|
+
}
|
|
4204
|
+
function safeStorage() {
|
|
4205
|
+
try {
|
|
4206
|
+
if (typeof window === "undefined") return null;
|
|
4207
|
+
const ls = window.localStorage;
|
|
4208
|
+
const probe = "__pm_probe__";
|
|
4209
|
+
ls.setItem(probe, probe);
|
|
4210
|
+
ls.removeItem(probe);
|
|
4211
|
+
return ls;
|
|
4212
|
+
} catch {
|
|
4213
|
+
return null;
|
|
4214
|
+
}
|
|
4215
|
+
}
|
|
4216
|
+
function readPayload(key) {
|
|
4217
|
+
const ls = safeStorage();
|
|
4218
|
+
if (!ls) return null;
|
|
4219
|
+
try {
|
|
4220
|
+
const raw = ls.getItem(key);
|
|
4221
|
+
if (!raw) return null;
|
|
4222
|
+
const parsed = JSON.parse(raw);
|
|
4223
|
+
if (!parsed || parsed.v !== 1) return null;
|
|
4224
|
+
return parsed;
|
|
4225
|
+
} catch {
|
|
4226
|
+
return null;
|
|
4227
|
+
}
|
|
4228
|
+
}
|
|
4229
|
+
function writePayload(key, payload) {
|
|
4230
|
+
const ls = safeStorage();
|
|
4231
|
+
if (!ls) return;
|
|
4232
|
+
try {
|
|
4233
|
+
ls.setItem(key, JSON.stringify(payload));
|
|
4234
|
+
} catch {
|
|
4235
|
+
}
|
|
4236
|
+
}
|
|
4237
|
+
function makeId() {
|
|
4238
|
+
const rand = Math.random().toString(36).slice(2, 10);
|
|
4239
|
+
return `v_${Date.now().toString(36)}_${rand}`;
|
|
4240
|
+
}
|
|
4241
|
+
function useSavedViews({
|
|
4242
|
+
storageKey,
|
|
4243
|
+
enabled,
|
|
4244
|
+
current
|
|
4245
|
+
}) {
|
|
4246
|
+
const initial = useMemo(
|
|
4247
|
+
() => enabled ? readPayload(storageKey) : null,
|
|
4248
|
+
[enabled, storageKey]
|
|
4249
|
+
);
|
|
4250
|
+
const [views, setViews] = useState(initial?.views ?? []);
|
|
4251
|
+
const [activeViewId, setActiveViewId] = useState(
|
|
4252
|
+
initial?.activeViewId ?? null
|
|
4253
|
+
);
|
|
4254
|
+
const restored = enabled ? initial?.current ?? null : null;
|
|
4255
|
+
const viewsRef = useRef(views);
|
|
4256
|
+
viewsRef.current = views;
|
|
4257
|
+
const activeViewIdRef = useRef(activeViewId);
|
|
4258
|
+
activeViewIdRef.current = activeViewId;
|
|
4259
|
+
useEffect(() => {
|
|
4260
|
+
if (!enabled) return;
|
|
4261
|
+
const handle = setTimeout(() => {
|
|
4262
|
+
writePayload(storageKey, {
|
|
4263
|
+
v: 1,
|
|
4264
|
+
current,
|
|
4265
|
+
views: viewsRef.current,
|
|
4266
|
+
activeViewId: activeViewIdRef.current
|
|
4267
|
+
});
|
|
4268
|
+
}, DEBOUNCE_MS);
|
|
4269
|
+
return () => clearTimeout(handle);
|
|
4270
|
+
}, [enabled, storageKey, current]);
|
|
4271
|
+
const flush = useCallback(
|
|
4272
|
+
(nextViews, nextActive) => {
|
|
4273
|
+
if (!enabled) return;
|
|
4274
|
+
writePayload(storageKey, {
|
|
4275
|
+
v: 1,
|
|
4276
|
+
current,
|
|
4277
|
+
views: nextViews,
|
|
4278
|
+
activeViewId: nextActive
|
|
4279
|
+
});
|
|
4280
|
+
},
|
|
4281
|
+
[enabled, storageKey, current]
|
|
4282
|
+
);
|
|
4283
|
+
const saveView = useCallback(
|
|
4284
|
+
(name) => {
|
|
4285
|
+
if (!enabled) return null;
|
|
4286
|
+
const trimmed = name.trim();
|
|
4287
|
+
if (!trimmed) return null;
|
|
4288
|
+
const id = makeId();
|
|
4289
|
+
const next = [
|
|
4290
|
+
...viewsRef.current,
|
|
4291
|
+
{ id, name: trimmed, state: current }
|
|
4292
|
+
];
|
|
4293
|
+
setViews(next);
|
|
4294
|
+
setActiveViewId(id);
|
|
4295
|
+
flush(next, id);
|
|
4296
|
+
return id;
|
|
4297
|
+
},
|
|
4298
|
+
[enabled, current, flush]
|
|
4299
|
+
);
|
|
4300
|
+
const applyView = useCallback(
|
|
4301
|
+
(id) => {
|
|
4302
|
+
const found = viewsRef.current.find((v) => v.id === id);
|
|
4303
|
+
if (!found) return null;
|
|
4304
|
+
setActiveViewId(id);
|
|
4305
|
+
flush(viewsRef.current, id);
|
|
4306
|
+
return found.state;
|
|
4307
|
+
},
|
|
4308
|
+
[flush]
|
|
4309
|
+
);
|
|
4310
|
+
const deleteView = useCallback(
|
|
4311
|
+
(id) => {
|
|
4312
|
+
const next = viewsRef.current.filter((v) => v.id !== id);
|
|
4313
|
+
const nextActive = activeViewIdRef.current === id ? null : activeViewIdRef.current;
|
|
4314
|
+
setViews(next);
|
|
4315
|
+
setActiveViewId(nextActive);
|
|
4316
|
+
flush(next, nextActive);
|
|
4317
|
+
},
|
|
4318
|
+
[flush]
|
|
4319
|
+
);
|
|
4320
|
+
const clearActiveView = useCallback(() => {
|
|
4321
|
+
if (activeViewIdRef.current == null) return;
|
|
4322
|
+
setActiveViewId(null);
|
|
4323
|
+
flush(viewsRef.current, null);
|
|
4324
|
+
}, [flush]);
|
|
4325
|
+
return {
|
|
4326
|
+
restored,
|
|
4327
|
+
views,
|
|
4328
|
+
activeViewId,
|
|
4329
|
+
saveView,
|
|
4330
|
+
applyView,
|
|
4331
|
+
deleteView,
|
|
4332
|
+
clearActiveView
|
|
4333
|
+
};
|
|
4334
|
+
}
|
|
3924
4335
|
function TasksPageCore({
|
|
3925
4336
|
workspaceId: workspaceIdProp,
|
|
3926
4337
|
projectId,
|
|
@@ -3946,19 +4357,26 @@ function TasksPageCore({
|
|
|
3946
4357
|
onTaskSaved,
|
|
3947
4358
|
createSurface = "modal",
|
|
3948
4359
|
detailSurface = "drawer",
|
|
4360
|
+
enableSavedViews,
|
|
4361
|
+
persistViewKey,
|
|
4362
|
+
enableShortcuts,
|
|
3949
4363
|
className
|
|
3950
4364
|
}) {
|
|
3951
4365
|
const ctx = usePlanMagnetContextSafe();
|
|
3952
4366
|
const workspaceId = workspaceIdProp ?? ctx?.workspaceId;
|
|
3953
4367
|
const client = useApolloClient();
|
|
3954
4368
|
const mergedLabels = mergeTaskLabels(labels);
|
|
4369
|
+
const savedViewsEnabled = Boolean(enableSavedViews || persistViewKey);
|
|
3955
4370
|
const [view, setView] = useState(defaultView);
|
|
3956
4371
|
const [search, setSearch] = useState("");
|
|
3957
4372
|
const [priority, setPriority] = useState("ALL");
|
|
3958
4373
|
const [type, setType] = useState("ALL");
|
|
3959
4374
|
const [mine, setMine] = useState(false);
|
|
4375
|
+
const [savedListColumns, setSavedListColumns] = useState(null);
|
|
3960
4376
|
const [creating, setCreating] = useState(false);
|
|
3961
4377
|
const [selectedId, setSelectedId] = useState(null);
|
|
4378
|
+
const [showHelp, setShowHelp] = useState(false);
|
|
4379
|
+
const searchInputRef = useRef(null);
|
|
3962
4380
|
const [createDefaults, setCreateDefaults] = useState(null);
|
|
3963
4381
|
useEffect(() => {
|
|
3964
4382
|
if (openItemId) setSelectedId(openItemId);
|
|
@@ -4010,6 +4428,169 @@ function TasksPageCore({
|
|
|
4010
4428
|
setSelectedId(task.id);
|
|
4011
4429
|
}, []);
|
|
4012
4430
|
const handleSelect = useCallback((id) => setSelectedId(id), []);
|
|
4431
|
+
const persistEnabled = savedViewsEnabled && Boolean(workspaceId);
|
|
4432
|
+
const storageKey = useMemo(
|
|
4433
|
+
() => makeStorageKey(workspaceId ?? "", persistViewKey),
|
|
4434
|
+
[workspaceId, persistViewKey]
|
|
4435
|
+
);
|
|
4436
|
+
const currentViewState = useMemo(
|
|
4437
|
+
() => ({
|
|
4438
|
+
view,
|
|
4439
|
+
priority,
|
|
4440
|
+
type,
|
|
4441
|
+
search,
|
|
4442
|
+
mine,
|
|
4443
|
+
listColumns: savedListColumns
|
|
4444
|
+
}),
|
|
4445
|
+
[view, priority, type, search, mine, savedListColumns]
|
|
4446
|
+
);
|
|
4447
|
+
const applyViewState = useCallback((s) => {
|
|
4448
|
+
setView(s.view);
|
|
4449
|
+
setPriority(s.priority);
|
|
4450
|
+
setType(s.type);
|
|
4451
|
+
setSearch(s.search);
|
|
4452
|
+
setMine(s.mine);
|
|
4453
|
+
setSavedListColumns(s.listColumns);
|
|
4454
|
+
}, []);
|
|
4455
|
+
const {
|
|
4456
|
+
restored,
|
|
4457
|
+
views: savedViews,
|
|
4458
|
+
activeViewId,
|
|
4459
|
+
saveView,
|
|
4460
|
+
applyView,
|
|
4461
|
+
deleteView,
|
|
4462
|
+
clearActiveView
|
|
4463
|
+
} = useSavedViews({
|
|
4464
|
+
storageKey,
|
|
4465
|
+
enabled: persistEnabled,
|
|
4466
|
+
current: currentViewState
|
|
4467
|
+
});
|
|
4468
|
+
const [didRestore, setDidRestore] = useState(false);
|
|
4469
|
+
useEffect(() => {
|
|
4470
|
+
if (didRestore || !persistEnabled) return;
|
|
4471
|
+
if (restored) applyViewState(restored);
|
|
4472
|
+
setDidRestore(true);
|
|
4473
|
+
}, [persistEnabled]);
|
|
4474
|
+
const handleApplySavedView = useCallback(
|
|
4475
|
+
(id) => {
|
|
4476
|
+
const s = applyView(id);
|
|
4477
|
+
if (s) applyViewState(s);
|
|
4478
|
+
},
|
|
4479
|
+
[applyView, applyViewState]
|
|
4480
|
+
);
|
|
4481
|
+
const detachOnEdit = useCallback(() => {
|
|
4482
|
+
if (activeViewId != null) clearActiveView();
|
|
4483
|
+
}, [activeViewId, clearActiveView]);
|
|
4484
|
+
const handleViewChange = useCallback(
|
|
4485
|
+
(v) => {
|
|
4486
|
+
setView(v);
|
|
4487
|
+
detachOnEdit();
|
|
4488
|
+
},
|
|
4489
|
+
[detachOnEdit]
|
|
4490
|
+
);
|
|
4491
|
+
const handleSearchChange = useCallback(
|
|
4492
|
+
(s) => {
|
|
4493
|
+
setSearch(s);
|
|
4494
|
+
detachOnEdit();
|
|
4495
|
+
},
|
|
4496
|
+
[detachOnEdit]
|
|
4497
|
+
);
|
|
4498
|
+
const handlePriorityChange = useCallback(
|
|
4499
|
+
(p) => {
|
|
4500
|
+
setPriority(p);
|
|
4501
|
+
detachOnEdit();
|
|
4502
|
+
},
|
|
4503
|
+
[detachOnEdit]
|
|
4504
|
+
);
|
|
4505
|
+
const handleTypeChange = useCallback(
|
|
4506
|
+
(t) => {
|
|
4507
|
+
setType(t);
|
|
4508
|
+
detachOnEdit();
|
|
4509
|
+
},
|
|
4510
|
+
[detachOnEdit]
|
|
4511
|
+
);
|
|
4512
|
+
const handleMineChange = useCallback(
|
|
4513
|
+
(m) => {
|
|
4514
|
+
setMine(m);
|
|
4515
|
+
detachOnEdit();
|
|
4516
|
+
},
|
|
4517
|
+
[detachOnEdit]
|
|
4518
|
+
);
|
|
4519
|
+
const enabledViews = useMemo(
|
|
4520
|
+
() => views ?? ["board", "list", "timeline"],
|
|
4521
|
+
[views]
|
|
4522
|
+
);
|
|
4523
|
+
const shortcutsCanCreate = !readonly && Boolean(projectId);
|
|
4524
|
+
useEffect(() => {
|
|
4525
|
+
if (!enableShortcuts) return;
|
|
4526
|
+
const onKeyDown = (e) => {
|
|
4527
|
+
if (e.ctrlKey || e.metaKey || e.altKey) return;
|
|
4528
|
+
const target = e.target;
|
|
4529
|
+
if (target) {
|
|
4530
|
+
const tag = target.tagName;
|
|
4531
|
+
if (tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || target.isContentEditable) {
|
|
4532
|
+
if (e.key !== "Escape") return;
|
|
4533
|
+
}
|
|
4534
|
+
}
|
|
4535
|
+
switch (e.key) {
|
|
4536
|
+
case "c":
|
|
4537
|
+
if (shortcutsCanCreate) {
|
|
4538
|
+
e.preventDefault();
|
|
4539
|
+
startCreate();
|
|
4540
|
+
}
|
|
4541
|
+
break;
|
|
4542
|
+
case "/":
|
|
4543
|
+
e.preventDefault();
|
|
4544
|
+
searchInputRef.current?.focus();
|
|
4545
|
+
break;
|
|
4546
|
+
case "b":
|
|
4547
|
+
if (enabledViews.includes("board")) {
|
|
4548
|
+
e.preventDefault();
|
|
4549
|
+
setView("board");
|
|
4550
|
+
}
|
|
4551
|
+
break;
|
|
4552
|
+
case "l":
|
|
4553
|
+
if (enabledViews.includes("list")) {
|
|
4554
|
+
e.preventDefault();
|
|
4555
|
+
setView("list");
|
|
4556
|
+
}
|
|
4557
|
+
break;
|
|
4558
|
+
case "g":
|
|
4559
|
+
if (enabledViews.includes("timeline")) {
|
|
4560
|
+
e.preventDefault();
|
|
4561
|
+
setView("timeline");
|
|
4562
|
+
}
|
|
4563
|
+
break;
|
|
4564
|
+
case "m":
|
|
4565
|
+
if (currentActorId) {
|
|
4566
|
+
e.preventDefault();
|
|
4567
|
+
setMine((prev) => !prev);
|
|
4568
|
+
}
|
|
4569
|
+
break;
|
|
4570
|
+
case "?":
|
|
4571
|
+
e.preventDefault();
|
|
4572
|
+
setShowHelp((v) => !v);
|
|
4573
|
+
break;
|
|
4574
|
+
case "Escape":
|
|
4575
|
+
if (showHelp) setShowHelp(false);
|
|
4576
|
+
if (creating) cancelCreate();
|
|
4577
|
+
if (selectedId != null) setSelectedId(null);
|
|
4578
|
+
break;
|
|
4579
|
+
}
|
|
4580
|
+
};
|
|
4581
|
+
document.addEventListener("keydown", onKeyDown);
|
|
4582
|
+
return () => document.removeEventListener("keydown", onKeyDown);
|
|
4583
|
+
}, [
|
|
4584
|
+
enableShortcuts,
|
|
4585
|
+
enabledViews,
|
|
4586
|
+
shortcutsCanCreate,
|
|
4587
|
+
currentActorId,
|
|
4588
|
+
creating,
|
|
4589
|
+
selectedId,
|
|
4590
|
+
showHelp,
|
|
4591
|
+
startCreate,
|
|
4592
|
+
cancelCreate
|
|
4593
|
+
]);
|
|
4013
4594
|
if (!workspaceId) {
|
|
4014
4595
|
return /* @__PURE__ */ jsx("div", { className: cn("p-6 text-sm text-destructive", className), children: "TasksPage: no workspaceId provided (prop or PlanMagnetProvider)." });
|
|
4015
4596
|
}
|
|
@@ -4050,32 +4631,37 @@ function TasksPageCore({
|
|
|
4050
4631
|
const listPriority = priority === "ALL" ? void 0 : priority;
|
|
4051
4632
|
const listType = type === "ALL" ? void 0 : type;
|
|
4052
4633
|
const mineAssignee = mine && currentActorId ? currentActorId : void 0;
|
|
4053
|
-
const
|
|
4054
|
-
|
|
4055
|
-
"status",
|
|
4056
|
-
"priority",
|
|
4057
|
-
"assignee",
|
|
4058
|
-
"dueDate",
|
|
4059
|
-
"linkedRecord"
|
|
4060
|
-
] : void 0;
|
|
4634
|
+
const derivedListColumns = linkSourceProduct ? ["title", "status", "priority", "assignee", "dueDate", "linkedRecord"] : void 0;
|
|
4635
|
+
const listColumns = savedListColumns ?? derivedListColumns;
|
|
4061
4636
|
return /* @__PURE__ */ jsx(TaskLabelsProvider, { value: mergedLabels, children: /* @__PURE__ */ jsxs("div", { className: cn("space-y-4 p-6", className), children: [
|
|
4062
4637
|
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 }),
|
|
4063
4638
|
/* @__PURE__ */ jsx(
|
|
4064
4639
|
TaskToolbar,
|
|
4065
4640
|
{
|
|
4066
4641
|
view,
|
|
4067
|
-
onViewChange: setView,
|
|
4642
|
+
onViewChange: savedViewsEnabled ? handleViewChange : setView,
|
|
4068
4643
|
views,
|
|
4069
4644
|
search,
|
|
4070
|
-
onSearchChange: setSearch,
|
|
4645
|
+
onSearchChange: savedViewsEnabled ? handleSearchChange : setSearch,
|
|
4646
|
+
searchInputRef: enableShortcuts ? searchInputRef : void 0,
|
|
4071
4647
|
priority,
|
|
4072
|
-
onPriorityChange: setPriority,
|
|
4648
|
+
onPriorityChange: savedViewsEnabled ? handlePriorityChange : setPriority,
|
|
4073
4649
|
type,
|
|
4074
|
-
onTypeChange: setType,
|
|
4650
|
+
onTypeChange: savedViewsEnabled ? handleTypeChange : setType,
|
|
4075
4651
|
onNewTask: startCreate,
|
|
4076
4652
|
canCreate,
|
|
4077
4653
|
mine,
|
|
4078
|
-
onMineChange: currentActorId ? setMine : void 0
|
|
4654
|
+
onMineChange: currentActorId ? savedViewsEnabled ? handleMineChange : setMine : void 0,
|
|
4655
|
+
leadingActions: savedViewsEnabled ? /* @__PURE__ */ jsx(
|
|
4656
|
+
SavedViewsMenu,
|
|
4657
|
+
{
|
|
4658
|
+
views: savedViews,
|
|
4659
|
+
activeViewId,
|
|
4660
|
+
onSaveView: saveView,
|
|
4661
|
+
onApplyView: handleApplySavedView,
|
|
4662
|
+
onDeleteView: deleteView
|
|
4663
|
+
}
|
|
4664
|
+
) : void 0
|
|
4079
4665
|
}
|
|
4080
4666
|
),
|
|
4081
4667
|
showInlineCreate && /* @__PURE__ */ jsx("div", { children: createForm }),
|
|
@@ -4153,9 +4739,60 @@ function TasksPageCore({
|
|
|
4153
4739
|
ariaLabel: "Task detail",
|
|
4154
4740
|
children: /* @__PURE__ */ jsx("div", { className: "p-2 pt-8", children: detailView })
|
|
4155
4741
|
}
|
|
4742
|
+
),
|
|
4743
|
+
enableShortcuts && /* @__PURE__ */ jsx(
|
|
4744
|
+
Modal,
|
|
4745
|
+
{
|
|
4746
|
+
open: showHelp,
|
|
4747
|
+
onClose: () => setShowHelp(false),
|
|
4748
|
+
ariaLabel: mergedLabels.keyboardShortcuts,
|
|
4749
|
+
className: "max-w-sm",
|
|
4750
|
+
children: /* @__PURE__ */ jsx(
|
|
4751
|
+
ShortcutsHelp,
|
|
4752
|
+
{
|
|
4753
|
+
labels: mergedLabels,
|
|
4754
|
+
enabledViews,
|
|
4755
|
+
canCreate,
|
|
4756
|
+
showMine: Boolean(currentActorId)
|
|
4757
|
+
}
|
|
4758
|
+
)
|
|
4759
|
+
}
|
|
4156
4760
|
)
|
|
4157
4761
|
] }) });
|
|
4158
4762
|
}
|
|
4763
|
+
function ShortcutRow({ keys, label }) {
|
|
4764
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4 py-1.5", children: [
|
|
4765
|
+
/* @__PURE__ */ jsx("span", { className: "text-sm text-foreground", children: label }),
|
|
4766
|
+
/* @__PURE__ */ jsx("span", { className: "flex items-center gap-1", children: keys.map((k) => /* @__PURE__ */ jsx(
|
|
4767
|
+
"kbd",
|
|
4768
|
+
{
|
|
4769
|
+
className: "inline-flex min-w-[1.5rem] items-center justify-center rounded border border-border bg-muted px-1.5 py-0.5 text-xs font-medium text-muted-foreground",
|
|
4770
|
+
children: k
|
|
4771
|
+
},
|
|
4772
|
+
k
|
|
4773
|
+
)) })
|
|
4774
|
+
] });
|
|
4775
|
+
}
|
|
4776
|
+
function ShortcutsHelp({
|
|
4777
|
+
labels: L,
|
|
4778
|
+
enabledViews,
|
|
4779
|
+
canCreate,
|
|
4780
|
+
showMine
|
|
4781
|
+
}) {
|
|
4782
|
+
return /* @__PURE__ */ jsxs("div", { className: "p-6 pt-8", children: [
|
|
4783
|
+
/* @__PURE__ */ jsx("h2", { className: "mb-3 text-base font-semibold text-foreground", children: L.keyboardShortcuts }),
|
|
4784
|
+
/* @__PURE__ */ jsxs("div", { className: "divide-y divide-border", children: [
|
|
4785
|
+
canCreate && /* @__PURE__ */ jsx(ShortcutRow, { keys: ["c"], label: L.shortcutNewTask }),
|
|
4786
|
+
/* @__PURE__ */ jsx(ShortcutRow, { keys: ["/"], label: L.shortcutSearch }),
|
|
4787
|
+
enabledViews.includes("board") && /* @__PURE__ */ jsx(ShortcutRow, { keys: ["b"], label: L.shortcutBoardView }),
|
|
4788
|
+
enabledViews.includes("list") && /* @__PURE__ */ jsx(ShortcutRow, { keys: ["l"], label: L.shortcutListView }),
|
|
4789
|
+
enabledViews.includes("timeline") && /* @__PURE__ */ jsx(ShortcutRow, { keys: ["g"], label: L.shortcutTimelineView }),
|
|
4790
|
+
showMine && /* @__PURE__ */ jsx(ShortcutRow, { keys: ["m"], label: L.shortcutMyTasks }),
|
|
4791
|
+
/* @__PURE__ */ jsx(ShortcutRow, { keys: ["?"], label: L.shortcutHelp }),
|
|
4792
|
+
/* @__PURE__ */ jsx(ShortcutRow, { keys: ["Esc"], label: L.shortcutClose })
|
|
4793
|
+
] })
|
|
4794
|
+
] });
|
|
4795
|
+
}
|
|
4159
4796
|
function TasksPage({
|
|
4160
4797
|
apiGatewayUrl,
|
|
4161
4798
|
authToken,
|
|
@@ -4185,6 +4822,6 @@ function TasksPage({
|
|
|
4185
4822
|
);
|
|
4186
4823
|
}
|
|
4187
4824
|
|
|
4188
|
-
export { Avatar, BULK_UPDATE_WORK_ITEMS, 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 };
|
|
4825
|
+
export { Avatar, BULK_UPDATE_WORK_ITEMS, 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, SavedViewsMenu, 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, makeStorageKey, mergeTaskLabels, resetPlanMagnetClient, resetPlanMagnetConfig, setPlanMagnetClient, setPlanMagnetConfig, useMilestones, usePlanMagnetContext, usePlanMagnetContextSafe, useSavedViews, useTaskLabels, useWorkItemLinks, validateTrustedOrigin, waitForPlanMagnetClient };
|
|
4189
4826
|
//# sourceMappingURL=index.js.map
|
|
4190
4827
|
//# sourceMappingURL=index.js.map
|