feat: star favoris dynamique + sidebar refresh sans reload
FlowDeck CI / test (push) Failing after 4s
FlowDeck CI / docker (push) Has been skipped

- page_editor: étoile ⭐/☆ dynamique selon favorited (x-text)
- toggleFavorite() appelle refreshFavorites() du sidebar
- base.html: refreshFavorites() fetch /api/library/favorites + rebuild DOM
- Context menu 'Add to Favorites' → refreshFavorites() au lieu de location.reload()
- Les items dynamiques ont onclick/oncontextmenu pour fonctionner hors Alpine
This commit is contained in:
2026-07-17 23:50:37 -04:00
parent 25386bc79f
commit fe64617677
2 changed files with 39 additions and 4 deletions
+35 -2
View File
@@ -601,6 +601,40 @@
// ── Favorites tracking ──
favoriteIds: new Set([{% for p in favorite_pages %}'{{ p.id }}'{% if not loop.last %},{% endif %}{% endfor %}]),
refreshFavorites() {
const self = this;
fetch('/api/library/favorites', {credentials:'same-origin'})
.then(r => r.json())
.then(data => {
const items = data.items || [];
self.favoriteIds = new Set(items.map(i => i.id));
const favList = document.querySelector('.sidebar-items[data-section="favorites"]');
if (!favList) return;
if (items.length === 0) {
favList.innerHTML = '<li class="sidebar-item empty-hint"><span class="page-icon">⭐</span><span class="page-name text-dim">No favorites yet</span></li>';
} else {
favList.innerHTML = items.map(function(item) {
const icon = item.icon || '📄';
const name = (item.title || 'Untitled');
const safeName = name.replace(/'/g, "\\'").replace(/"/g, '&quot;');
const url = item.url || ('/pages/' + item.id);
return '<li class="sidebar-item" style="padding-left:8px;cursor:pointer" ' +
'data-page-id="' + item.id + '" data-page-depth="0" data-has-children="false" ' +
'onclick="window.appState.navigateTo(\'' + url + '\')" ' +
'oncontextmenu="event.preventDefault();window.appState.showContextMenu(event,\'' + item.id + '\',\'' + safeName + '\')">' +
'<span class="tree-toggle-placeholder"></span>' +
'<span class="page-icon">' + icon + '</span>' +
'<span class="page-name">' + safeName + '</span>' +
'<span class="item-actions">' +
'<button class="item-action-btn" title="View in Library" onclick="event.stopPropagation();window.appState.navigateTo(\'/library\')">📚</button>' +
'<button class="item-action-btn" title="New sub-page" onclick="event.stopPropagation();window.appState.newSubPage(\'' + item.id + '\')">+</button>' +
'</span>' +
'</li>';
}).join('');
}
});
},
// ── Sections collapsible ──
sectionsOpen: (function() {
@@ -727,8 +761,7 @@
if (isFav) self.favoriteIds.delete(pageId);
else self.favoriteIds.add(pageId);
self.contextMenu.visible = false;
// Reload to update sidebar
location.reload();
self.refreshFavorites();
})
.catch(e => this.toast('Favorite action failed', 'error'));
break;