feat(archive): completed cards move to a mirror of the boards folder
"Archive completed tasks" empties every board of what is finished into a twin of the boards folder - `Boards/work.md` into `Boards/archive/work.md` - itself a Kanban board with the same lanes, so the history opens like any other board and the live one stays short. The same pass lifts a board's native archive (everything below the `***` separator) into that mirror, so finished work is not left sitting in two places. Archived tasks render beside live ones when "Include the archive" and "Show completed" are both on, from the mirrors and from any `***` section still in a board. They carry an Archive badge instead of a project chip, ride with the sphere they came from in the rail, and refuse drops.
This commit is contained in:
1 parent
0cc4655f9f
commit
9c0e57e29b
12 files changed
+680
-122
No files matched your search
+53
-1
@@ -9,6 +9,7 @@ import { JSDOM } from "jsdom";
|
||||
const root = new URL("..", import.meta.url).pathname;
|
||||
const fixtures = join(root, "tests/fixtures");
|
||||
const FOLDER = "Boards";
|
||||
const ARCHIVE = "archive";
|
||||
|
||||
const out = join(mkdtempSync(join(tmpdir(), "bcal-")), "bundle.mjs");
|
||||
writeFileSync(
|
||||
@@ -96,6 +97,23 @@ const files = new Map(
|
||||
readFileSync(join(fixtures, name), "utf8"),
|
||||
]),
|
||||
);
|
||||
// A mirror file, the way the archive command leaves one behind.
|
||||
const MIRROR = `${FOLDER}/${ARCHIVE}/personal.md`;
|
||||
files.set(
|
||||
MIRROR,
|
||||
[
|
||||
"---",
|
||||
"",
|
||||
"kanban-plugin: board",
|
||||
"",
|
||||
"---",
|
||||
"",
|
||||
"## errands",
|
||||
"",
|
||||
"- [x] hang the shelf 📅 2026-07-02 ✅ 2026-07-03",
|
||||
"",
|
||||
].join("\n"),
|
||||
);
|
||||
const handles = new Map([...files.keys()].map((path) => [path, new TFile(path)]));
|
||||
|
||||
const writes = [];
|
||||
@@ -122,7 +140,7 @@ const app = {
|
||||
|
||||
const settings = {
|
||||
boardsFolder: FOLDER,
|
||||
archiveHeading: "Archive",
|
||||
archiveFolder: ARCHIVE,
|
||||
view: "list",
|
||||
groupBy: "sphere",
|
||||
listSort: "due",
|
||||
@@ -153,6 +171,11 @@ await store.reloadAll();
|
||||
const outsideArchive = 15;
|
||||
check("parsed every board", store.spheres.length === names.length, `${store.spheres.length}`);
|
||||
check(`${outsideArchive} tasks outside the archive`, store.all.length === outsideArchive, `got ${store.all.length}`);
|
||||
check(
|
||||
"the mirror file is not a sphere of its own",
|
||||
store.spheres.every((sphere) => !sphere.path.includes(`/${ARCHIVE}/`)),
|
||||
store.spheres.map((sphere) => sphere.path).join(", "),
|
||||
);
|
||||
|
||||
const target = dom.window.document.body;
|
||||
const instance = mount(App, {
|
||||
@@ -181,10 +204,39 @@ flushSync();
|
||||
await settle();
|
||||
check("archive appears when both toggles are on", text().includes("pick up the parcel"));
|
||||
check("archived task carries the Archive badge", text().includes("Archive"));
|
||||
check("the archive folder is rendered too", text().includes("hang the shelf"));
|
||||
|
||||
const mirrored = store.all.find((task) => task.text === "hang the shelf");
|
||||
check("a mirrored task is archive", mirrored?.inArchive === true);
|
||||
check(
|
||||
"it keeps the lane it was archived from",
|
||||
mirrored?.project === "errands",
|
||||
`${mirrored?.project}`,
|
||||
);
|
||||
check(
|
||||
"it groups under the board it mirrors, not beside it",
|
||||
store.groups.filter((group) => group.label === "personal").length === 1,
|
||||
store.groups.map((group) => group.label).join(", "),
|
||||
);
|
||||
|
||||
// The rail knows nothing of the mirror file, so archived cards have to ride
|
||||
// with the sphere they came from.
|
||||
store.soloSphere(`${FOLDER}/personal.md`);
|
||||
flushSync();
|
||||
await settle();
|
||||
check("the rail keeps mirrored cards with their sphere", text().includes("hang the shelf"));
|
||||
store.soloSphere(`${FOLDER}/work.md`);
|
||||
flushSync();
|
||||
await settle();
|
||||
check("another sphere leaves them out", !text().includes("hang the shelf"));
|
||||
store.filterAll();
|
||||
flushSync();
|
||||
await settle();
|
||||
|
||||
store.includeArchive = false;
|
||||
flushSync();
|
||||
check("archive hidden again", !text().includes("pick up the parcel"));
|
||||
check("the archive folder is hidden with it", !text().includes("hang the shelf"));
|
||||
store.showDone = false;
|
||||
flushSync();
|
||||
|
||||
|
||||
Reference in new issue
Block a user