feat(KB-069): add Archive All Done feature
- Add archiveAllDone method to TaskStore with filtering and batch archive - Add POST /tasks/archive-all-done API endpoint with tests - Add useTasks hook support and API function for archiveAllDone - Add Archive All button to done column header in Board UI - Wire up onArchiveAllDone through Board component hierarchy
This commit is contained in:
@@ -30,10 +30,14 @@ vi.mock("../../api", () => ({
|
||||
mergeTask: vi.fn(),
|
||||
retryTask: vi.fn(),
|
||||
updateTask: vi.fn(),
|
||||
archiveTask: vi.fn(),
|
||||
unarchiveTask: vi.fn(),
|
||||
archiveAllDone: vi.fn(),
|
||||
}));
|
||||
|
||||
const mockFetchTasks = vi.mocked(api.fetchTasks);
|
||||
const mockUpdateTask = vi.mocked(api.updateTask);
|
||||
const mockArchiveAllDone = vi.mocked(api.archiveAllDone);
|
||||
|
||||
// Mock EventSource
|
||||
class MockEventSource {
|
||||
@@ -643,4 +647,57 @@ describe("useTasks", () => {
|
||||
expect(result.current.tasks[0].dependencies).toEqual(["KB-002", "KB-003"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("archiveAllDone", () => {
|
||||
it("archives all done tasks and updates local state", async () => {
|
||||
const doneTasks = [
|
||||
createMockTask({ id: "KB-001", column: "done" as Column }),
|
||||
createMockTask({ id: "KB-002", column: "done" as Column }),
|
||||
];
|
||||
const todoTask = createMockTask({ id: "KB-003", column: "todo" as Column });
|
||||
mockFetchTasks.mockResolvedValueOnce([...doneTasks, todoTask]);
|
||||
|
||||
const archivedTasks = [
|
||||
createMockTask({ id: "KB-001", column: "archived" as Column }),
|
||||
createMockTask({ id: "KB-002", column: "archived" as Column }),
|
||||
];
|
||||
mockArchiveAllDone.mockResolvedValueOnce(archivedTasks);
|
||||
|
||||
const { result } = renderHook(() => useTasks());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.tasks).toHaveLength(3);
|
||||
});
|
||||
|
||||
await act(async () => {
|
||||
await result.current.archiveAllDone();
|
||||
});
|
||||
|
||||
expect(mockArchiveAllDone).toHaveBeenCalled();
|
||||
// Done tasks should be archived
|
||||
expect(result.current.tasks.find((t) => t.id === "KB-001")?.column).toBe("archived");
|
||||
expect(result.current.tasks.find((t) => t.id === "KB-002")?.column).toBe("archived");
|
||||
// Todo task should remain unchanged
|
||||
expect(result.current.tasks.find((t) => t.id === "KB-003")?.column).toBe("todo");
|
||||
});
|
||||
|
||||
it("returns empty array when no done tasks exist", async () => {
|
||||
const todoTask = createMockTask({ id: "KB-001", column: "todo" as Column });
|
||||
mockFetchTasks.mockResolvedValueOnce([todoTask]);
|
||||
mockArchiveAllDone.mockResolvedValueOnce([]);
|
||||
|
||||
const { result } = renderHook(() => useTasks());
|
||||
|
||||
await waitFor(() => {
|
||||
expect(result.current.tasks).toHaveLength(1);
|
||||
});
|
||||
|
||||
const archived = await act(async () => {
|
||||
return await result.current.archiveAllDone();
|
||||
});
|
||||
|
||||
expect(archived).toEqual([]);
|
||||
expect(result.current.tasks[0].column).toBe("todo");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -214,5 +214,18 @@ export function useTasks() {
|
||||
return task;
|
||||
}, []);
|
||||
|
||||
return { tasks, createTask, moveTask, deleteTask, mergeTask, retryTask, duplicateTask, updateTask, archiveTask, unarchiveTask };
|
||||
const archiveAllDone = useCallback(async (): Promise<Task[]> => {
|
||||
const archived = await api.archiveAllDone();
|
||||
const normalized = archived.map(normalizeTask);
|
||||
// Update local state by mapping over tasks and updating archived ones
|
||||
setTasks((prev) =>
|
||||
prev.map((t) => {
|
||||
const updated = normalized.find((archived) => archived.id === t.id);
|
||||
return updated || t;
|
||||
})
|
||||
);
|
||||
return normalized;
|
||||
}, []);
|
||||
|
||||
return { tasks, createTask, moveTask, deleteTask, mergeTask, retryTask, duplicateTask, updateTask, archiveTask, unarchiveTask, archiveAllDone };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user