@industry-theme/backlogmd-kanban-panel 1.0.1 → 1.0.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/panels/KanbanPanel.d.ts.map +1 -1
- package/dist/panels/kanban/components/KanbanColumn.d.ts +8 -0
- package/dist/panels/kanban/components/KanbanColumn.d.ts.map +1 -1
- package/dist/panels/kanban/hooks/useKanbanData.d.ts +14 -1
- package/dist/panels/kanban/hooks/useKanbanData.d.ts.map +1 -1
- package/dist/panels.bundle.js +453 -129
- package/dist/panels.bundle.js.map +1 -1
- package/package.json +2 -2
package/dist/panels.bundle.js
CHANGED
|
@@ -684,6 +684,28 @@ const PRIORITY_ORDER = {
|
|
|
684
684
|
medium: 1,
|
|
685
685
|
low: 2
|
|
686
686
|
};
|
|
687
|
+
function sortTasksByTitle(tasks, direction = "asc") {
|
|
688
|
+
return [...tasks].sort((a, b) => {
|
|
689
|
+
const cmp = a.title.localeCompare(b.title);
|
|
690
|
+
return direction === "asc" ? cmp : -cmp;
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
function sortTasksBy(tasks, sortBy = "title", direction = "asc") {
|
|
694
|
+
switch (sortBy) {
|
|
695
|
+
case "title":
|
|
696
|
+
return sortTasksByTitle(tasks, direction);
|
|
697
|
+
case "createdDate":
|
|
698
|
+
return [...tasks].sort((a, b) => {
|
|
699
|
+
const cmp = a.createdDate.localeCompare(b.createdDate);
|
|
700
|
+
return direction === "asc" ? cmp : -cmp;
|
|
701
|
+
});
|
|
702
|
+
case "priority":
|
|
703
|
+
case "ordinal":
|
|
704
|
+
default:
|
|
705
|
+
const sorted = sortTasks(tasks);
|
|
706
|
+
return direction === "desc" ? sorted.reverse() : sorted;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
687
709
|
function sortTasks(tasks) {
|
|
688
710
|
return [...tasks].sort((a, b) => {
|
|
689
711
|
if (a.ordinal !== void 0 && b.ordinal !== void 0) {
|
|
@@ -863,6 +885,99 @@ class Core {
|
|
|
863
885
|
this.ensureInitialized();
|
|
864
886
|
return this.tasks.get(id);
|
|
865
887
|
}
|
|
888
|
+
/**
|
|
889
|
+
* List tasks with pagination
|
|
890
|
+
*
|
|
891
|
+
* @param filter - Filter and pagination options
|
|
892
|
+
* @returns Paginated result with tasks
|
|
893
|
+
*/
|
|
894
|
+
listTasksPaginated(filter) {
|
|
895
|
+
this.ensureInitialized();
|
|
896
|
+
let tasks = this.applyFilters(Array.from(this.tasks.values()), filter);
|
|
897
|
+
const pagination = (filter == null ? void 0 : filter.pagination) ?? {};
|
|
898
|
+
const sortBy = pagination.sortBy ?? "title";
|
|
899
|
+
const sortDirection = pagination.sortDirection ?? "asc";
|
|
900
|
+
tasks = sortTasksBy(tasks, sortBy, sortDirection);
|
|
901
|
+
const limit = pagination.limit ?? 10;
|
|
902
|
+
const offset = pagination.offset ?? 0;
|
|
903
|
+
const total = tasks.length;
|
|
904
|
+
const items = tasks.slice(offset, offset + limit);
|
|
905
|
+
return {
|
|
906
|
+
items,
|
|
907
|
+
total,
|
|
908
|
+
hasMore: offset + limit < total,
|
|
909
|
+
offset,
|
|
910
|
+
limit
|
|
911
|
+
};
|
|
912
|
+
}
|
|
913
|
+
/**
|
|
914
|
+
* Get tasks by status with pagination per column
|
|
915
|
+
*
|
|
916
|
+
* @param pagination - Pagination options (applied per status)
|
|
917
|
+
* @returns Paginated tasks grouped by status
|
|
918
|
+
*/
|
|
919
|
+
getTasksByStatusPaginated(pagination) {
|
|
920
|
+
this.ensureInitialized();
|
|
921
|
+
const limit = (pagination == null ? void 0 : pagination.limit) ?? 10;
|
|
922
|
+
const offset = (pagination == null ? void 0 : pagination.offset) ?? 0;
|
|
923
|
+
const sortBy = (pagination == null ? void 0 : pagination.sortBy) ?? "title";
|
|
924
|
+
const sortDirection = (pagination == null ? void 0 : pagination.sortDirection) ?? "asc";
|
|
925
|
+
const byStatus = /* @__PURE__ */ new Map();
|
|
926
|
+
const allGrouped = /* @__PURE__ */ new Map();
|
|
927
|
+
for (const status of this.config.statuses) {
|
|
928
|
+
allGrouped.set(status, []);
|
|
929
|
+
}
|
|
930
|
+
for (const task of this.tasks.values()) {
|
|
931
|
+
const list = allGrouped.get(task.status);
|
|
932
|
+
if (list) {
|
|
933
|
+
list.push(task);
|
|
934
|
+
} else {
|
|
935
|
+
allGrouped.set(task.status, [task]);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
for (const status of this.config.statuses) {
|
|
939
|
+
let tasks = allGrouped.get(status) ?? [];
|
|
940
|
+
tasks = sortTasksBy(tasks, sortBy, sortDirection);
|
|
941
|
+
const total = tasks.length;
|
|
942
|
+
const items = tasks.slice(offset, offset + limit);
|
|
943
|
+
byStatus.set(status, {
|
|
944
|
+
items,
|
|
945
|
+
total,
|
|
946
|
+
hasMore: offset + limit < total,
|
|
947
|
+
offset,
|
|
948
|
+
limit
|
|
949
|
+
});
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
byStatus,
|
|
953
|
+
statuses: this.config.statuses
|
|
954
|
+
};
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Load more tasks for a specific status
|
|
958
|
+
*
|
|
959
|
+
* @param status - Status column to load more from
|
|
960
|
+
* @param currentOffset - Current offset (items already loaded)
|
|
961
|
+
* @param pagination - Pagination options (limit, sortBy, sortDirection)
|
|
962
|
+
* @returns Paginated result for the status
|
|
963
|
+
*/
|
|
964
|
+
loadMoreForStatus(status, currentOffset, pagination) {
|
|
965
|
+
this.ensureInitialized();
|
|
966
|
+
const limit = (pagination == null ? void 0 : pagination.limit) ?? 10;
|
|
967
|
+
const sortBy = (pagination == null ? void 0 : pagination.sortBy) ?? "title";
|
|
968
|
+
const sortDirection = (pagination == null ? void 0 : pagination.sortDirection) ?? "asc";
|
|
969
|
+
let tasks = Array.from(this.tasks.values()).filter((t) => t.status === status);
|
|
970
|
+
tasks = sortTasksBy(tasks, sortBy, sortDirection);
|
|
971
|
+
const total = tasks.length;
|
|
972
|
+
const items = tasks.slice(currentOffset, currentOffset + limit);
|
|
973
|
+
return {
|
|
974
|
+
items,
|
|
975
|
+
total,
|
|
976
|
+
hasMore: currentOffset + limit < total,
|
|
977
|
+
offset: currentOffset,
|
|
978
|
+
limit
|
|
979
|
+
};
|
|
980
|
+
}
|
|
866
981
|
/**
|
|
867
982
|
* Reload all tasks from disk
|
|
868
983
|
*
|
|
@@ -879,6 +994,27 @@ class Core {
|
|
|
879
994
|
throw new Error("Core not initialized. Call initialize() first.");
|
|
880
995
|
}
|
|
881
996
|
}
|
|
997
|
+
applyFilters(tasks, filter) {
|
|
998
|
+
if (!filter)
|
|
999
|
+
return tasks;
|
|
1000
|
+
let result = tasks;
|
|
1001
|
+
if (filter.status) {
|
|
1002
|
+
result = result.filter((t) => t.status === filter.status);
|
|
1003
|
+
}
|
|
1004
|
+
if (filter.assignee) {
|
|
1005
|
+
result = result.filter((t) => t.assignee.includes(filter.assignee));
|
|
1006
|
+
}
|
|
1007
|
+
if (filter.priority) {
|
|
1008
|
+
result = result.filter((t) => t.priority === filter.priority);
|
|
1009
|
+
}
|
|
1010
|
+
if (filter.labels && filter.labels.length > 0) {
|
|
1011
|
+
result = result.filter((t) => filter.labels.some((label) => t.labels.includes(label)));
|
|
1012
|
+
}
|
|
1013
|
+
if (filter.parentTaskId) {
|
|
1014
|
+
result = result.filter((t) => t.parentTaskId === filter.parentTaskId);
|
|
1015
|
+
}
|
|
1016
|
+
return result;
|
|
1017
|
+
}
|
|
882
1018
|
async loadTasksFromDirectory(dir, source) {
|
|
883
1019
|
const entries = await this.fs.readDir(dir);
|
|
884
1020
|
for (const entry of entries) {
|
|
@@ -1020,8 +1156,9 @@ class PanelFileSystemAdapter {
|
|
|
1020
1156
|
}
|
|
1021
1157
|
}
|
|
1022
1158
|
const DEFAULT_STATUSES = ["To Do", "In Progress", "Done"];
|
|
1159
|
+
const DEFAULT_PAGE_SIZE = 10;
|
|
1023
1160
|
function useKanbanData(options) {
|
|
1024
|
-
const { context, actions } = options || {};
|
|
1161
|
+
const { context, actions, pageSize = DEFAULT_PAGE_SIZE } = options || {};
|
|
1025
1162
|
const [tasks, setTasks] = useState([]);
|
|
1026
1163
|
const [statuses, setStatuses] = useState(DEFAULT_STATUSES);
|
|
1027
1164
|
const [isLoading, setIsLoading] = useState(true);
|
|
@@ -1030,6 +1167,10 @@ function useKanbanData(options) {
|
|
|
1030
1167
|
const [tasksByStatus, setTasksByStatus] = useState(
|
|
1031
1168
|
/* @__PURE__ */ new Map()
|
|
1032
1169
|
);
|
|
1170
|
+
const [columnStates, setColumnStates] = useState(
|
|
1171
|
+
/* @__PURE__ */ new Map()
|
|
1172
|
+
);
|
|
1173
|
+
const coreRef = useRef(null);
|
|
1033
1174
|
const activeFilePathRef = useRef(null);
|
|
1034
1175
|
const contextRef = useRef(context);
|
|
1035
1176
|
const actionsRef = useRef(actions);
|
|
@@ -1074,22 +1215,26 @@ function useKanbanData(options) {
|
|
|
1074
1215
|
setTasks([]);
|
|
1075
1216
|
setStatuses(DEFAULT_STATUSES);
|
|
1076
1217
|
setTasksByStatus(/* @__PURE__ */ new Map());
|
|
1218
|
+
setColumnStates(/* @__PURE__ */ new Map());
|
|
1077
1219
|
setIsLoading(false);
|
|
1220
|
+
coreRef.current = null;
|
|
1078
1221
|
return;
|
|
1079
1222
|
}
|
|
1080
1223
|
setIsLoading(true);
|
|
1081
1224
|
setError(null);
|
|
1082
1225
|
try {
|
|
1083
1226
|
const fileTreeSlice = context.getRepositorySlice("fileTree");
|
|
1084
|
-
if (!((_a = fileTreeSlice == null ? void 0 : fileTreeSlice.data) == null ? void 0 : _a.
|
|
1227
|
+
if (!((_a = fileTreeSlice == null ? void 0 : fileTreeSlice.data) == null ? void 0 : _a.allFiles)) {
|
|
1085
1228
|
console.log("[useKanbanData] FileTree not available");
|
|
1086
1229
|
setIsBacklogProject(false);
|
|
1087
1230
|
setTasks([]);
|
|
1088
1231
|
setStatuses(DEFAULT_STATUSES);
|
|
1089
1232
|
setTasksByStatus(/* @__PURE__ */ new Map());
|
|
1233
|
+
setColumnStates(/* @__PURE__ */ new Map());
|
|
1234
|
+
coreRef.current = null;
|
|
1090
1235
|
return;
|
|
1091
1236
|
}
|
|
1092
|
-
const files = fileTreeSlice.data.
|
|
1237
|
+
const files = fileTreeSlice.data.allFiles;
|
|
1093
1238
|
const filePaths = files.map((f) => f.path);
|
|
1094
1239
|
const fs = new PanelFileSystemAdapter({
|
|
1095
1240
|
fetchFile: fetchFileContent,
|
|
@@ -1106,20 +1251,56 @@ function useKanbanData(options) {
|
|
|
1106
1251
|
setTasks([]);
|
|
1107
1252
|
setStatuses(DEFAULT_STATUSES);
|
|
1108
1253
|
setTasksByStatus(/* @__PURE__ */ new Map());
|
|
1254
|
+
setColumnStates(/* @__PURE__ */ new Map());
|
|
1255
|
+
coreRef.current = null;
|
|
1109
1256
|
return;
|
|
1110
1257
|
}
|
|
1111
|
-
console.log("[useKanbanData] Loading Backlog.md data...");
|
|
1258
|
+
console.log("[useKanbanData] Loading Backlog.md data with pagination...");
|
|
1112
1259
|
setIsBacklogProject(true);
|
|
1113
1260
|
await core.initialize();
|
|
1261
|
+
coreRef.current = core;
|
|
1114
1262
|
const config = core.getConfig();
|
|
1115
|
-
const
|
|
1116
|
-
|
|
1263
|
+
const paginatedResult = core.getTasksByStatusPaginated({
|
|
1264
|
+
limit: pageSize,
|
|
1265
|
+
offset: 0,
|
|
1266
|
+
sortBy: "title",
|
|
1267
|
+
sortDirection: "asc"
|
|
1268
|
+
});
|
|
1269
|
+
const newTasksByStatus = /* @__PURE__ */ new Map();
|
|
1270
|
+
const newColumnStates = /* @__PURE__ */ new Map();
|
|
1271
|
+
let allTasks = [];
|
|
1272
|
+
for (const status of paginatedResult.statuses) {
|
|
1273
|
+
const columnResult = paginatedResult.byStatus.get(status);
|
|
1274
|
+
if (columnResult) {
|
|
1275
|
+
newTasksByStatus.set(status, columnResult.items);
|
|
1276
|
+
newColumnStates.set(status, {
|
|
1277
|
+
tasks: columnResult.items,
|
|
1278
|
+
total: columnResult.total,
|
|
1279
|
+
hasMore: columnResult.hasMore,
|
|
1280
|
+
isLoadingMore: false
|
|
1281
|
+
});
|
|
1282
|
+
allTasks = allTasks.concat(columnResult.items);
|
|
1283
|
+
} else {
|
|
1284
|
+
newTasksByStatus.set(status, []);
|
|
1285
|
+
newColumnStates.set(status, {
|
|
1286
|
+
tasks: [],
|
|
1287
|
+
total: 0,
|
|
1288
|
+
hasMore: false,
|
|
1289
|
+
isLoadingMore: false
|
|
1290
|
+
});
|
|
1291
|
+
}
|
|
1292
|
+
}
|
|
1293
|
+
const totalTasks = Array.from(paginatedResult.byStatus.values()).reduce(
|
|
1294
|
+
(sum, col) => sum + col.total,
|
|
1295
|
+
0
|
|
1296
|
+
);
|
|
1117
1297
|
console.log(
|
|
1118
|
-
`[useKanbanData] Loaded ${allTasks.length} tasks with ${config.statuses.length} statuses`
|
|
1298
|
+
`[useKanbanData] Loaded ${allTasks.length}/${totalTasks} tasks with ${config.statuses.length} statuses (page size: ${pageSize})`
|
|
1119
1299
|
);
|
|
1120
1300
|
setStatuses(config.statuses);
|
|
1121
1301
|
setTasks(allTasks);
|
|
1122
|
-
setTasksByStatus(
|
|
1302
|
+
setTasksByStatus(newTasksByStatus);
|
|
1303
|
+
setColumnStates(newColumnStates);
|
|
1123
1304
|
} catch (err) {
|
|
1124
1305
|
console.error("[useKanbanData] Failed to load Backlog.md data:", err);
|
|
1125
1306
|
setError(err instanceof Error ? err.message : "Failed to load backlog data");
|
|
@@ -1127,13 +1308,84 @@ function useKanbanData(options) {
|
|
|
1127
1308
|
setTasks([]);
|
|
1128
1309
|
setStatuses(DEFAULT_STATUSES);
|
|
1129
1310
|
setTasksByStatus(/* @__PURE__ */ new Map());
|
|
1311
|
+
setColumnStates(/* @__PURE__ */ new Map());
|
|
1312
|
+
coreRef.current = null;
|
|
1130
1313
|
} finally {
|
|
1131
1314
|
setIsLoading(false);
|
|
1132
1315
|
}
|
|
1133
|
-
}, [context, actions, fetchFileContent]);
|
|
1316
|
+
}, [context, actions, fetchFileContent, pageSize]);
|
|
1134
1317
|
useEffect(() => {
|
|
1135
1318
|
loadBacklogData();
|
|
1136
1319
|
}, [loadBacklogData]);
|
|
1320
|
+
const loadMore = useCallback(
|
|
1321
|
+
async (status) => {
|
|
1322
|
+
const core = coreRef.current;
|
|
1323
|
+
if (!core) {
|
|
1324
|
+
console.warn("[useKanbanData] Core not available for loadMore");
|
|
1325
|
+
return;
|
|
1326
|
+
}
|
|
1327
|
+
const currentState = columnStates.get(status);
|
|
1328
|
+
if (!currentState || !currentState.hasMore || currentState.isLoadingMore) {
|
|
1329
|
+
return;
|
|
1330
|
+
}
|
|
1331
|
+
setColumnStates((prev) => {
|
|
1332
|
+
const newStates = new Map(prev);
|
|
1333
|
+
const state = newStates.get(status);
|
|
1334
|
+
if (state) {
|
|
1335
|
+
newStates.set(status, { ...state, isLoadingMore: true });
|
|
1336
|
+
}
|
|
1337
|
+
return newStates;
|
|
1338
|
+
});
|
|
1339
|
+
try {
|
|
1340
|
+
const currentOffset = currentState.tasks.length;
|
|
1341
|
+
const result = core.loadMoreForStatus(
|
|
1342
|
+
status,
|
|
1343
|
+
currentOffset,
|
|
1344
|
+
{
|
|
1345
|
+
limit: pageSize,
|
|
1346
|
+
sortBy: "title",
|
|
1347
|
+
sortDirection: "asc"
|
|
1348
|
+
}
|
|
1349
|
+
);
|
|
1350
|
+
console.log(
|
|
1351
|
+
`[useKanbanData] Loaded ${result.items.length} more tasks for "${status}" (${currentOffset + result.items.length}/${result.total})`
|
|
1352
|
+
);
|
|
1353
|
+
setColumnStates((prev) => {
|
|
1354
|
+
const newStates = new Map(prev);
|
|
1355
|
+
const state = newStates.get(status);
|
|
1356
|
+
if (state) {
|
|
1357
|
+
const newTasks = [...state.tasks, ...result.items];
|
|
1358
|
+
newStates.set(status, {
|
|
1359
|
+
tasks: newTasks,
|
|
1360
|
+
total: result.total,
|
|
1361
|
+
hasMore: result.hasMore,
|
|
1362
|
+
isLoadingMore: false
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
return newStates;
|
|
1366
|
+
});
|
|
1367
|
+
setTasksByStatus((prev) => {
|
|
1368
|
+
const newMap = new Map(prev);
|
|
1369
|
+
const currentTasks = newMap.get(status) || [];
|
|
1370
|
+
newMap.set(status, [...currentTasks, ...result.items]);
|
|
1371
|
+
return newMap;
|
|
1372
|
+
});
|
|
1373
|
+
setTasks((prev) => [...prev, ...result.items]);
|
|
1374
|
+
} catch (err) {
|
|
1375
|
+
console.error(`[useKanbanData] Failed to load more for "${status}":`, err);
|
|
1376
|
+
setError(err instanceof Error ? err.message : "Failed to load more tasks");
|
|
1377
|
+
setColumnStates((prev) => {
|
|
1378
|
+
const newStates = new Map(prev);
|
|
1379
|
+
const state = newStates.get(status);
|
|
1380
|
+
if (state) {
|
|
1381
|
+
newStates.set(status, { ...state, isLoadingMore: false });
|
|
1382
|
+
}
|
|
1383
|
+
return newStates;
|
|
1384
|
+
});
|
|
1385
|
+
}
|
|
1386
|
+
},
|
|
1387
|
+
[columnStates, pageSize]
|
|
1388
|
+
);
|
|
1137
1389
|
const refreshData = useCallback(async () => {
|
|
1138
1390
|
await loadBacklogData();
|
|
1139
1391
|
}, [loadBacklogData]);
|
|
@@ -1154,6 +1406,8 @@ function useKanbanData(options) {
|
|
|
1154
1406
|
error,
|
|
1155
1407
|
isBacklogProject,
|
|
1156
1408
|
tasksByStatus,
|
|
1409
|
+
columnStates,
|
|
1410
|
+
loadMore,
|
|
1157
1411
|
refreshData,
|
|
1158
1412
|
updateTaskStatus
|
|
1159
1413
|
};
|
|
@@ -1161,6 +1415,10 @@ function useKanbanData(options) {
|
|
|
1161
1415
|
const KanbanColumn = ({
|
|
1162
1416
|
status,
|
|
1163
1417
|
tasks,
|
|
1418
|
+
total,
|
|
1419
|
+
hasMore = false,
|
|
1420
|
+
isLoadingMore = false,
|
|
1421
|
+
onLoadMore,
|
|
1164
1422
|
onTaskClick
|
|
1165
1423
|
}) => {
|
|
1166
1424
|
const { theme: theme2 } = useTheme();
|
|
@@ -1176,6 +1434,7 @@ const KanbanColumn = ({
|
|
|
1176
1434
|
return theme2.colors.border;
|
|
1177
1435
|
}
|
|
1178
1436
|
};
|
|
1437
|
+
const remaining = total !== void 0 ? total - tasks.length : 0;
|
|
1179
1438
|
return /* @__PURE__ */ jsxs(
|
|
1180
1439
|
"div",
|
|
1181
1440
|
{
|
|
@@ -1224,13 +1483,13 @@ const KanbanColumn = ({
|
|
|
1224
1483
|
padding: "2px 8px",
|
|
1225
1484
|
borderRadius: theme2.radii[1]
|
|
1226
1485
|
},
|
|
1227
|
-
children: tasks.length
|
|
1486
|
+
children: total !== void 0 ? `${tasks.length}/${total}` : tasks.length
|
|
1228
1487
|
}
|
|
1229
1488
|
)
|
|
1230
1489
|
]
|
|
1231
1490
|
}
|
|
1232
1491
|
),
|
|
1233
|
-
/* @__PURE__ */
|
|
1492
|
+
/* @__PURE__ */ jsxs(
|
|
1234
1493
|
"div",
|
|
1235
1494
|
{
|
|
1236
1495
|
style: {
|
|
@@ -1241,131 +1500,190 @@ const KanbanColumn = ({
|
|
|
1241
1500
|
overflowY: "auto",
|
|
1242
1501
|
WebkitOverflowScrolling: "touch"
|
|
1243
1502
|
},
|
|
1244
|
-
children:
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
},
|
|
1265
|
-
onMouseLeave: (e) => {
|
|
1266
|
-
if (onTaskClick) {
|
|
1267
|
-
e.currentTarget.style.transform = "translateY(0)";
|
|
1268
|
-
e.currentTarget.style.boxShadow = "none";
|
|
1269
|
-
}
|
|
1270
|
-
},
|
|
1271
|
-
children: [
|
|
1272
|
-
/* @__PURE__ */ jsx(
|
|
1273
|
-
"h4",
|
|
1274
|
-
{
|
|
1275
|
-
style: {
|
|
1276
|
-
margin: "0 0 8px 0",
|
|
1277
|
-
fontSize: theme2.fontSizes[2],
|
|
1278
|
-
color: theme2.colors.text,
|
|
1279
|
-
fontWeight: theme2.fontWeights.medium
|
|
1280
|
-
},
|
|
1281
|
-
children: task.title
|
|
1503
|
+
children: [
|
|
1504
|
+
tasks.map((task) => /* @__PURE__ */ jsxs(
|
|
1505
|
+
"div",
|
|
1506
|
+
{
|
|
1507
|
+
onClick: () => onTaskClick == null ? void 0 : onTaskClick(task),
|
|
1508
|
+
style: {
|
|
1509
|
+
background: theme2.colors.surface,
|
|
1510
|
+
borderRadius: theme2.radii[2],
|
|
1511
|
+
padding: "12px",
|
|
1512
|
+
border: `1px solid ${theme2.colors.border}`,
|
|
1513
|
+
borderLeft: `4px solid ${getPriorityColor(task.priority)}`,
|
|
1514
|
+
cursor: onTaskClick ? "pointer" : "default",
|
|
1515
|
+
transition: "all 0.2s ease",
|
|
1516
|
+
minHeight: "44px"
|
|
1517
|
+
// Minimum touch target size for mobile
|
|
1518
|
+
},
|
|
1519
|
+
onMouseEnter: (e) => {
|
|
1520
|
+
if (onTaskClick) {
|
|
1521
|
+
e.currentTarget.style.transform = "translateY(-2px)";
|
|
1522
|
+
e.currentTarget.style.boxShadow = `0 4px 8px ${theme2.colors.border}`;
|
|
1282
1523
|
}
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
style
|
|
1288
|
-
margin: "0 0 8px 0",
|
|
1289
|
-
fontSize: theme2.fontSizes[1],
|
|
1290
|
-
color: theme2.colors.textSecondary,
|
|
1291
|
-
overflow: "hidden",
|
|
1292
|
-
textOverflow: "ellipsis",
|
|
1293
|
-
display: "-webkit-box",
|
|
1294
|
-
WebkitLineClamp: 2,
|
|
1295
|
-
WebkitBoxOrient: "vertical",
|
|
1296
|
-
lineHeight: "1.4"
|
|
1297
|
-
},
|
|
1298
|
-
children: task.description
|
|
1524
|
+
},
|
|
1525
|
+
onMouseLeave: (e) => {
|
|
1526
|
+
if (onTaskClick) {
|
|
1527
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
1528
|
+
e.currentTarget.style.boxShadow = "none";
|
|
1299
1529
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
children: task.labels.map((label) => /* @__PURE__ */ jsx(
|
|
1311
|
-
"span",
|
|
1312
|
-
{
|
|
1313
|
-
style: {
|
|
1314
|
-
fontSize: theme2.fontSizes[0],
|
|
1315
|
-
color: theme2.colors.primary,
|
|
1316
|
-
background: `${theme2.colors.primary}20`,
|
|
1317
|
-
padding: "2px 8px",
|
|
1318
|
-
borderRadius: theme2.radii[1],
|
|
1319
|
-
fontWeight: theme2.fontWeights.medium
|
|
1320
|
-
},
|
|
1321
|
-
children: label
|
|
1530
|
+
},
|
|
1531
|
+
children: [
|
|
1532
|
+
/* @__PURE__ */ jsx(
|
|
1533
|
+
"h4",
|
|
1534
|
+
{
|
|
1535
|
+
style: {
|
|
1536
|
+
margin: "0 0 8px 0",
|
|
1537
|
+
fontSize: theme2.fontSizes[2],
|
|
1538
|
+
color: theme2.colors.text,
|
|
1539
|
+
fontWeight: theme2.fontWeights.medium
|
|
1322
1540
|
},
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1541
|
+
children: task.title
|
|
1542
|
+
}
|
|
1543
|
+
),
|
|
1544
|
+
task.description && /* @__PURE__ */ jsx(
|
|
1545
|
+
"p",
|
|
1546
|
+
{
|
|
1547
|
+
style: {
|
|
1548
|
+
margin: "0 0 8px 0",
|
|
1549
|
+
fontSize: theme2.fontSizes[1],
|
|
1550
|
+
color: theme2.colors.textSecondary,
|
|
1551
|
+
overflow: "hidden",
|
|
1552
|
+
textOverflow: "ellipsis",
|
|
1553
|
+
display: "-webkit-box",
|
|
1554
|
+
WebkitLineClamp: 2,
|
|
1555
|
+
WebkitBoxOrient: "vertical",
|
|
1556
|
+
lineHeight: "1.4"
|
|
1557
|
+
},
|
|
1558
|
+
children: task.description
|
|
1559
|
+
}
|
|
1560
|
+
),
|
|
1561
|
+
task.labels && task.labels.length > 0 && /* @__PURE__ */ jsx(
|
|
1562
|
+
"div",
|
|
1563
|
+
{
|
|
1564
|
+
style: {
|
|
1565
|
+
display: "flex",
|
|
1566
|
+
gap: "4px",
|
|
1567
|
+
flexWrap: "wrap",
|
|
1568
|
+
marginBottom: "8px"
|
|
1569
|
+
},
|
|
1570
|
+
children: task.labels.map((label) => /* @__PURE__ */ jsx(
|
|
1348
1571
|
"span",
|
|
1349
1572
|
{
|
|
1350
1573
|
style: {
|
|
1351
|
-
|
|
1574
|
+
fontSize: theme2.fontSizes[0],
|
|
1575
|
+
color: theme2.colors.primary,
|
|
1576
|
+
background: `${theme2.colors.primary}20`,
|
|
1577
|
+
padding: "2px 8px",
|
|
1578
|
+
borderRadius: theme2.radii[1],
|
|
1579
|
+
fontWeight: theme2.fontWeights.medium
|
|
1352
1580
|
},
|
|
1353
|
-
children:
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1581
|
+
children: label
|
|
1582
|
+
},
|
|
1583
|
+
label
|
|
1584
|
+
))
|
|
1585
|
+
}
|
|
1586
|
+
),
|
|
1587
|
+
/* @__PURE__ */ jsxs(
|
|
1588
|
+
"div",
|
|
1589
|
+
{
|
|
1590
|
+
style: {
|
|
1591
|
+
display: "flex",
|
|
1592
|
+
alignItems: "center",
|
|
1593
|
+
justifyContent: "space-between",
|
|
1594
|
+
fontSize: theme2.fontSizes[0],
|
|
1595
|
+
color: theme2.colors.textMuted
|
|
1596
|
+
},
|
|
1597
|
+
children: [
|
|
1598
|
+
/* @__PURE__ */ jsx(
|
|
1599
|
+
"span",
|
|
1600
|
+
{
|
|
1601
|
+
style: {
|
|
1602
|
+
fontFamily: theme2.fonts.monospace
|
|
1603
|
+
},
|
|
1604
|
+
children: task.id
|
|
1605
|
+
}
|
|
1606
|
+
),
|
|
1607
|
+
task.assignee && task.assignee.length > 0 && /* @__PURE__ */ jsxs(
|
|
1608
|
+
"span",
|
|
1609
|
+
{
|
|
1610
|
+
style: {
|
|
1611
|
+
color: theme2.colors.textSecondary
|
|
1612
|
+
},
|
|
1613
|
+
children: [
|
|
1614
|
+
task.assignee.length,
|
|
1615
|
+
" assignee",
|
|
1616
|
+
task.assignee.length !== 1 ? "s" : ""
|
|
1617
|
+
]
|
|
1618
|
+
}
|
|
1619
|
+
)
|
|
1620
|
+
]
|
|
1621
|
+
}
|
|
1622
|
+
)
|
|
1623
|
+
]
|
|
1624
|
+
},
|
|
1625
|
+
task.id
|
|
1626
|
+
)),
|
|
1627
|
+
hasMore && onLoadMore && /* @__PURE__ */ jsx(
|
|
1628
|
+
"button",
|
|
1629
|
+
{
|
|
1630
|
+
onClick: onLoadMore,
|
|
1631
|
+
disabled: isLoadingMore,
|
|
1632
|
+
style: {
|
|
1633
|
+
background: theme2.colors.background,
|
|
1634
|
+
border: `1px dashed ${theme2.colors.border}`,
|
|
1635
|
+
borderRadius: theme2.radii[2],
|
|
1636
|
+
padding: "12px",
|
|
1637
|
+
cursor: isLoadingMore ? "wait" : "pointer",
|
|
1638
|
+
color: theme2.colors.textSecondary,
|
|
1639
|
+
fontSize: theme2.fontSizes[1],
|
|
1640
|
+
fontWeight: theme2.fontWeights.medium,
|
|
1641
|
+
transition: "all 0.2s ease",
|
|
1642
|
+
minHeight: "44px",
|
|
1643
|
+
display: "flex",
|
|
1644
|
+
alignItems: "center",
|
|
1645
|
+
justifyContent: "center",
|
|
1646
|
+
gap: "8px"
|
|
1647
|
+
},
|
|
1648
|
+
onMouseEnter: (e) => {
|
|
1649
|
+
if (!isLoadingMore) {
|
|
1650
|
+
e.currentTarget.style.background = theme2.colors.backgroundSecondary;
|
|
1651
|
+
e.currentTarget.style.borderColor = theme2.colors.primary;
|
|
1652
|
+
e.currentTarget.style.color = theme2.colors.primary;
|
|
1361
1653
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1654
|
+
},
|
|
1655
|
+
onMouseLeave: (e) => {
|
|
1656
|
+
e.currentTarget.style.background = theme2.colors.background;
|
|
1657
|
+
e.currentTarget.style.borderColor = theme2.colors.border;
|
|
1658
|
+
e.currentTarget.style.color = theme2.colors.textSecondary;
|
|
1659
|
+
},
|
|
1660
|
+
children: isLoadingMore ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1661
|
+
/* @__PURE__ */ jsx(
|
|
1662
|
+
"span",
|
|
1663
|
+
{
|
|
1664
|
+
style: {
|
|
1665
|
+
display: "inline-block",
|
|
1666
|
+
width: "14px",
|
|
1667
|
+
height: "14px",
|
|
1668
|
+
border: `2px solid ${theme2.colors.border}`,
|
|
1669
|
+
borderTopColor: theme2.colors.primary,
|
|
1670
|
+
borderRadius: "50%",
|
|
1671
|
+
animation: "spin 1s linear infinite"
|
|
1672
|
+
}
|
|
1673
|
+
}
|
|
1674
|
+
),
|
|
1675
|
+
"Loading..."
|
|
1676
|
+
] }) : `Load more (${remaining} remaining)`
|
|
1677
|
+
}
|
|
1678
|
+
)
|
|
1679
|
+
]
|
|
1367
1680
|
}
|
|
1368
|
-
)
|
|
1681
|
+
),
|
|
1682
|
+
/* @__PURE__ */ jsx("style", { children: `
|
|
1683
|
+
@keyframes spin {
|
|
1684
|
+
to { transform: rotate(360deg); }
|
|
1685
|
+
}
|
|
1686
|
+
` })
|
|
1369
1687
|
]
|
|
1370
1688
|
}
|
|
1371
1689
|
);
|
|
@@ -1626,9 +1944,10 @@ const KanbanPanelContent = ({
|
|
|
1626
1944
|
var _a, _b;
|
|
1627
1945
|
const { theme: theme2 } = useTheme();
|
|
1628
1946
|
const [_selectedTask, setSelectedTask] = useState(null);
|
|
1629
|
-
const { statuses, tasksByStatus, error, isBacklogProject, refreshData } = useKanbanData({
|
|
1947
|
+
const { statuses, tasksByStatus, columnStates, loadMore, error, isBacklogProject, refreshData } = useKanbanData({
|
|
1630
1948
|
context,
|
|
1631
|
-
actions
|
|
1949
|
+
actions,
|
|
1950
|
+
pageSize: 10
|
|
1632
1951
|
});
|
|
1633
1952
|
const handleTaskClick = (task) => {
|
|
1634
1953
|
setSelectedTask(task);
|
|
@@ -1774,11 +2093,16 @@ const KanbanPanelContent = ({
|
|
|
1774
2093
|
},
|
|
1775
2094
|
children: statuses.map((status) => {
|
|
1776
2095
|
const columnTasks = tasksByStatus.get(status) || [];
|
|
2096
|
+
const columnState = columnStates.get(status);
|
|
1777
2097
|
return /* @__PURE__ */ jsx(
|
|
1778
2098
|
KanbanColumn,
|
|
1779
2099
|
{
|
|
1780
2100
|
status,
|
|
1781
2101
|
tasks: columnTasks,
|
|
2102
|
+
total: columnState == null ? void 0 : columnState.total,
|
|
2103
|
+
hasMore: columnState == null ? void 0 : columnState.hasMore,
|
|
2104
|
+
isLoadingMore: columnState == null ? void 0 : columnState.isLoadingMore,
|
|
2105
|
+
onLoadMore: () => loadMore(status),
|
|
1782
2106
|
onTaskClick: handleTaskClick
|
|
1783
2107
|
},
|
|
1784
2108
|
status
|