Skip to content

Commit 2a39054

Browse files
committed
fix: remove deleted labels from tasks
1 parent f930da2 commit 2a39054

3 files changed

Lines changed: 33 additions & 5 deletions

File tree

apps/web/server/boardRepo.ts

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,11 +191,21 @@ export async function deleteBoardLabel(db: D1, boardId: string, name: string): P
191191
if (!board) return null;
192192
const labels = parseBoard(board).labels;
193193
if (!labels.some((label) => label.name === name)) throw new HTTPException(404, { message: `Label not found: ${name}` });
194-
const inUse = await db
195-
.prepare("SELECT 1 FROM tasks WHERE board_id = ? AND EXISTS (SELECT 1 FROM json_each(tasks.labels) WHERE json_each.value = ?) LIMIT 1")
196-
.bind(boardId, name)
197-
.first();
198-
if (inUse) throw new HTTPException(409, { message: `Label is in use: ${name}` });
194+
195+
const tasks = await db
196+
.prepare("SELECT id, labels FROM tasks WHERE board_id = ? AND labels IS NOT NULL")
197+
.bind(boardId)
198+
.all<{ id: string; labels: string }>();
199+
const now = new Date().toISOString();
200+
const statements = tasks.results
201+
.map((task) => {
202+
const current = JSON.parse(task.labels) as string[];
203+
return { id: task.id, current, next: current.filter((label) => label !== name) };
204+
})
205+
.filter((task) => task.current.length !== task.next.length)
206+
.map((task) => db.prepare("UPDATE tasks SET labels = ?, updated_at = ? WHERE id = ?").bind(JSON.stringify(task.next), now, task.id));
207+
if (statements.length > 0) await db.batch(statements);
208+
199209
return updateBoard(db, boardId, { labels: labels.filter((label) => label.name !== name) });
200210
}
201211

apps/web/src/routes/AccountSettingsPage.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ function BoardItem({ board }: { board: any }) {
5959

6060
async function handleDeleteLabel(name: string) {
6161
setLabelError(null);
62+
const confirmed = window.confirm(`Delete label "${name}" from this board and remove it from all tasks?`);
63+
if (!confirmed) return;
6264
try {
6365
await deleteLabel.mutateAsync({ boardId: board.id, name });
6466
} catch (err) {

tests/task-json-fields.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,4 +197,20 @@ describe("task JSON field parsing (labels, input)", () => {
197197
expect(Array.isArray(reviewed!.labels)).toBe(true);
198198
expect(typeof reviewed!.input).toBe("object");
199199
});
200+
201+
it("deleteBoardLabel removes the label from tasks on the same board", async () => {
202+
const { createTask, getTask } = await import("../apps/web/server/taskRepo");
203+
const { deleteBoardLabel } = await import("../apps/web/server/boardRepo");
204+
const task = await createTask(db, ownerId, {
205+
title: "Delete label propagation",
206+
board_id: boardId,
207+
labels: ["bug", "feature"],
208+
});
209+
210+
const board = await deleteBoardLabel(db, boardId, "bug");
211+
const updated = await getTask(db, task.id, ownerId);
212+
213+
expect(board!.labels.map((label) => label.name)).not.toContain("bug");
214+
expect(updated!.labels).toEqual(["feature"]);
215+
});
200216
});

0 commit comments

Comments
 (0)