From 51c6002f089ed285330ca7cf5e0fd617aa46ee25 Mon Sep 17 00:00:00 2001 From: bruno Date: Mon, 13 Jul 2026 11:34:43 -0400 Subject: [PATCH] =?UTF-8?q?fix:=205=20bugs=20=E2=80=94=20couleur=20tag,=20?= =?UTF-8?q?tags=20existants,=20modal=20settings,=20rename,=20filtrage?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. ctxAddNewTag lit window._ctxMenuData.ctxNewTagColor (pas this.ctxNewTagColor) → la couleur sélectionnée dans le menu est maintenant sauvegardée 2. onContextMenu: charge workspaceTags si vide avant de calculer ctxAvailTags → les tags existants apparaissent dans le dropdown 3. Settings: CSS modal-overlay/modal-box/.btn-danger ajouté → le modal delete n'est plus transparent 4. Rename tag: modal avec input au lieu de prompt() → double-clic → modal avec couleur + input + autofocus 5. doFilter: _filterTreeByTag filtre displayTree quand tagFilter est actif → le filtrage par tags fonctionne dans toutes les vues --- app/templates/local_workspace.html | 27 +++++++++++++++-- app/templates/settings.html | 48 +++++++++++++++++++++++++++--- 2 files changed, 69 insertions(+), 6 deletions(-) diff --git a/app/templates/local_workspace.html b/app/templates/local_workspace.html index 93d22e3..f343130 100644 --- a/app/templates/local_workspace.html +++ b/app/templates/local_workspace.html @@ -751,6 +751,10 @@ window._wsInitData = (function() { if (!id) { menu.style.display = 'none'; return; } var node = this._findNodeById(this.displayTree, id); if (!node) { menu.style.display = 'none'; return; } + // Ensure workspace tags are loaded + if (!this.workspaceTags || this.workspaceTags.length === 0) { + this.loadWorkspaceTags(); + } // Store in Alpine data for menu actions (ctxMenuOpenPage, etc.) this.ctxMenu = {visible:true, x:ev.clientX, y:ev.clientY, node:node, tags:[...(node.tags||[])]}; this.ctxTagAdding = false; @@ -868,7 +872,7 @@ window._wsInitData = (function() { if (!tagName) return; var node = this.ctxMenu.node; if (!node) return; - color = color || this.ctxNewTagColor; + color = color || window._ctxMenuData.ctxNewTagColor; var r = await fetch('/api/local-workspace/items/' + node.id + '/tags', { method: 'POST', headers: {'Content-Type':'application/json'}, body: JSON.stringify({name: tagName.trim(), color: color}) @@ -1393,9 +1397,28 @@ window._wsInitData = (function() { var q = (this.searchQuery || '').toLowerCase().trim(); var ft = this.filterType || ''; if (!q && !ft && !this.tagFilter) { this.displayTree = [...this.tree]; this._computeFilteredTableItems(); return; } - this.displayTree = [...this.tree]; + if (this.tagFilter) { + // Filter tree by tag + this.displayTree = this._filterTreeByTag([...this.tree], this.tagFilter); + } else { + this.displayTree = [...this.tree]; + } this._computeFilteredTableItems(); }, + _filterTreeByTag(nodes, tagName) { + var result = []; + for (var i=0; i 0) { + var clone = Object.assign({}, n); + clone.children = filteredChildren; + result.push(clone); + } + } + return result; + }, filterByType(type) { this.filterType = (this.filterType === type) ? '' : type; this.doFilter(); diff --git a/app/templates/settings.html b/app/templates/settings.html index ce8b447..8f86f6c 100644 --- a/app/templates/settings.html +++ b/app/templates/settings.html @@ -57,6 +57,13 @@ .color-swatch:hover{transform:scale(1.15);} .color-swatch.selected{border-color:var(--text);box-shadow:0 0 0 2px var(--accent);} +/* Modal (for tag delete/rename) */ +.modal-overlay{position:fixed;inset:0;background:rgba(0,0,0,.6);display:flex;align-items:center;justify-content:center;z-index:999;backdrop-filter:blur(4px);} +.modal-box{background:var(--bg);border:1px solid var(--border-strong);border-radius:12px;padding:24px;min-width:320px;box-shadow:0 12px 40px rgba(0,0,0,.5);} +.modal-box h3{font-size:16px;font-weight:600;color:var(--text);} +.btn-danger{background:#E03E3E;color:#fff;border:none;padding:6px 16px;border-radius:6px;cursor:pointer;font-size:13px;} +.btn-danger:hover{background:#C93535;} + /* Close button */ .settings-close{position:absolute;top:12px;right:12px;width:32px;height:32px;background:none;border:none;color:var(--text-dim);font-size:20px;cursor:pointer;border-radius:6px;display:flex;align-items:center;justify-content:center;} .settings-close:hover{background:var(--bg-hover);color:var(--text);} @@ -218,7 +225,7 @@

Are you sure you want to delete the tag ""? - ⚠️ This tag is used on item(s). Removing it will also remove it from those items. + ⚠️ This tag is used on item(s).

@@ -227,6 +234,22 @@
+ + @@ -288,6 +311,8 @@ document.addEventListener('alpine:init', function() { newTagName: '', newTagColor: '#787774', deletingTag: null, + renamingTag: null, + renameValue: '', presetColors: ['#787774','#E03E3E','#D9730D','#DFAB01','#0F7B6C','#0B6E99','#6940A5','#AD1A72','#E16259','#D4A72C','#448361','#337EA9','#9065B0','#C94D8B'], avatarUrl: '{{ avatar_url or "" }}', avatarColor: '{{ avatar_color or "#3A3A3A" }}', @@ -332,12 +357,27 @@ document.addEventListener('alpine:init', function() { }, async renameTag(tag) { - var newName = prompt('Rename tag "' + tag.name + '":', tag.name); - if (!newName || !newName.trim() || newName.trim().toLowerCase() === tag.name.toLowerCase()) return; + this.renamingTag = tag; + this.renameValue = tag.name; + // Focus the input after Alpine renders the modal + var self = this; + this.$nextTick(function() { + var input = document.querySelector('.modal-box input[type=text]'); + if (input) { input.focus(); input.select(); } + }); + }, + + async confirmRenameTag() { + var tag = this.renamingTag; + var newName = this.renameValue.trim(); + if (!tag || !newName || newName.toLowerCase() === tag.name.toLowerCase()) { + this.renamingTag = null; return; + } await fetch('/api/settings/tags/' + tag.id, { method: 'PUT', headers: {'Content-Type':'application/json'}, - body: JSON.stringify({name: newName.trim()}) + body: JSON.stringify({name: newName}) }); + this.renamingTag = null; await this.loadTags(); },