M6: label management — usage counts + merge
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Successful in 8s
CI & Build / Python tests (push) Successful in 16s
CI & Build / Build & push image (push) Successful in 50s

Extends the existing label manager (create/rename/color/delete) with the two missing maintenance tools (task 1904), so the label list stays clean — which matters more now that #tags mint labels automatically.

Backend: GET /api/labels returns a per-label note count (one grouped query); new POST /api/labels/<id>/merge moves the source label's notes onto a target and deletes the source (repoint via delete+reinsert to avoid mutating the composite PK; preserves via_tag; dedupes notes already on the target). Body #tags are NOT rewritten, so a tag-sourced label re-mints on next edit if its #tag text remains — a documented nuance.

Frontend: LabelsModal shows each label's note count and a 'merge into…' picker; also restores the previously-missing close (x) and merge icons.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
This commit is contained in:
2026-07-22 12:36:50 -04:00
co-authored by Claude Opus 4.8
parent 95b0e30fc7
commit ae0c748507
5 changed files with 158 additions and 15 deletions
+9
View File
@@ -21,3 +21,12 @@ async def test_set_note_labels_requires_auth(app):
json={"label_ids": []},
)
assert resp.status_code == 401
async def test_merge_label_requires_auth(app):
client = app.test_client()
resp = await client.post(
"/api/labels/00000000-0000-0000-0000-000000000000/merge",
json={"into": "00000000-0000-0000-0000-000000000001"},
)
assert resp.status_code == 401