fix(workspace): compteurs de tags dynamiques + counts scoped par workspace
FlowDeck CI / lint (push) Successful in 53s
FlowDeck CI / test (push) Successful in 5m47s
FlowDeck CI / docker (push) Successful in 48s

This commit is contained in:
2026-09-14 20:26:27 -04:00
parent 98af112ba1
commit 0d475c3d2d
3 changed files with 149 additions and 8 deletions
+13 -7
View File
@@ -1900,7 +1900,10 @@ async def list_all_tags_global(request: Request):
"WHERE t.user_id = ? GROUP BY t.id ORDER BY t.name",
(uid,),
).fetchall()
return {"tags": [dict(r) for r in rows]}
return JSONResponse(
{"tags": [dict(r) for r in rows]},
headers={"Cache-Control": "no-store"},
)
# ═══════════ Workspace Tags API ═══════════
@@ -1915,14 +1918,17 @@ async def list_tags(request: Request):
return {"tags": []}
with get_conn() as conn:
rows = conn.execute(
"SELECT t.id, t.name, t.color, COUNT(pt.page_id) as count "
"FROM tags t "
"LEFT JOIN page_tags pt ON pt.tag_id = t.id "
"LEFT JOIN pages p ON p.id = pt.page_id AND p.workspace_id = ? "
"WHERE t.user_id = ? GROUP BY t.id ORDER BY t.name",
"SELECT t.id, t.name, t.color, "
"(SELECT COUNT(*) FROM page_tags pt "
" JOIN pages p ON p.id = pt.page_id AND p.workspace_id = ? "
" WHERE pt.tag_id = t.id) as count "
"FROM tags t WHERE t.user_id = ? ORDER BY t.name",
(ws_id, uid),
).fetchall()
return {"tags": [dict(r) for r in rows]}
return JSONResponse(
{"tags": [dict(r) for r in rows]},
headers={"Cache-Control": "no-store"},
)
@router.get("/api/local-workspace/items/{item_id:int}/tags")
+23 -1
View File
@@ -1207,6 +1207,7 @@ window._wsInitData = (function() {
this._updateNodeTags(this.tree, node.id, d.tag, 'add');
this.displayTree = [...this.tree];
this.tagsVersion++;
this._bumpTagCount(d.tag, 1);
await this.loadWorkspaceTags();
this._computeFilteredTableItems();
this._ctxRefreshAvail();
@@ -2269,12 +2270,31 @@ window._wsInitData = (function() {
// ── Tag management ──
async loadWorkspaceTags() {
try {
var r = await fetch('/api/local-workspace/tags');
var r = await fetch('/api/local-workspace/tags', { cache: 'no-store' });
var d = await r.json();
this.workspaceTags = d.tags || [];
} catch(e) { this.workspaceTags = []; }
},
// Mise à jour OPTIMISTE du compteur d'un tag dans la barre de tags :
// rafraîchit immédiatement la pastille (delta +1/-1) sans attendre le
// re-fetch (qui viendra ensuite confirmer/surécrire avec les valeurs serveur).
_bumpTagCount(tag, delta) {
if (!tag) return;
var tags = (this.workspaceTags || []).slice();
var i = -1;
for (var k = 0; k < tags.length; k++) {
if (String(tags[k].id) === String(tag.id)) { i = k; break; }
}
if (i >= 0) {
tags[i] = Object.assign({}, tags[i], { count: Math.max(0, (tags[i].count || 0) + delta) });
} else if (delta > 0) {
tags.push({ id: tag.id, name: tag.name || '', color: tag.color || '#787774', count: delta });
}
// Réassigne un nouveau tableau → réactivité Alpine
this.workspaceTags = tags;
},
toggleTagFilter(tagName) {
this.tagFilter = (this.tagFilter === tagName) ? '' : tagName;
this.filterType = '';
@@ -2306,6 +2326,7 @@ window._wsInitData = (function() {
this._updateNodeTags(this.tree, itemId, d.tag, 'add');
this.displayTree = [...this.tree];
this.tagsVersion++;
this._bumpTagCount(d.tag, 1);
await this.loadWorkspaceTags();
this._computeFilteredTableItems();
}
@@ -2322,6 +2343,7 @@ window._wsInitData = (function() {
this._updateNodeTags(this.tree, itemId, null, 'remove', tagId);
this.displayTree = [...this.tree];
this.tagsVersion++;
this._bumpTagCount({ id: tagId }, -1);
await this.loadWorkspaceTags();
this._computeFilteredTableItems();
}