feat: complete favorites system — sidebar, library, context menu, API
6 files changed:
- db.py: migrate favorites table FK from collection_pages(id) to pages(id)
- board.py: add favorites API (POST/DELETE /board/api/favorites/{id}, GET list)
- board.py: _sidebar_data() now loads favorite_pages from DB via JOIN
- dashboard.py: library_page loads lib_favorites from DB (not parent_section)
- csrf.py: exclude /board/api/favorites from CSRF checks
- base.html: context menu toggles Add/Remove Favorites based on state
- base.html: favoriteIds Alpine set initialized from server-rendered favorites
- test_app.py: test_favorites_crud rewritten for new page-based favorites API
Favorites now work end-to-end:
- Right-click → Add to Favorites (or Remove if already favorited)
- Sidebar Favorites section shows favorited pages
- Library Favorites tab shows the same pages
- API: POST/DELETE /board/api/favorites/{page_id}, GET /board/api/favorites
This commit is contained in:
+18
-2
@@ -289,7 +289,8 @@
|
||||
</div>
|
||||
<div class="context-menu-section">
|
||||
<div class="context-menu-item" @click="contextAction('favorite')">
|
||||
<span class="menu-icon">⭐</span> Remove from Favorites
|
||||
<span class="menu-icon">⭐</span>
|
||||
<span x-text="favoriteIds.has(contextMenu.pageId) ? 'Remove from Favorites' : 'Add to Favorites'"></span>
|
||||
</div>
|
||||
<div class="context-menu-item" @click="contextAction('recent')">
|
||||
<span class="menu-icon">👁️</span> Remove from Recents
|
||||
@@ -383,6 +384,9 @@
|
||||
contextActions: {},
|
||||
showNewPageMenu: false,
|
||||
|
||||
// ── Favorites tracking ──
|
||||
favoriteIds: new Set([{% for p in favorite_pages %}'{{ p.id }}'{% if not loop.last %},{% endif %}{% endfor %}]),
|
||||
|
||||
// ── Sections collapsible ──
|
||||
sectionsOpen: { recents: true, private: true, meetings: true, favorites: true, agents: true, shared: true, apps: true },
|
||||
toggleSection(key) { this.sectionsOpen[key] = !this.sectionsOpen[key]; this.sectionsOpen = Object.assign({}, this.sectionsOpen); },
|
||||
@@ -477,10 +481,22 @@
|
||||
const { pageId, pageName } = this.contextMenu;
|
||||
const self = this;
|
||||
const csrf = this.getCsrfToken();
|
||||
const dbId = pageId.startsWith('page/') ? parseInt(pageId.split('/')[1]) : parseInt(pageId);
|
||||
|
||||
switch(action) {
|
||||
case 'favorite':
|
||||
alert(`Toggled favorite for: ${pageName}`);
|
||||
const isFav = this.favoriteIds.has(pageId);
|
||||
const method = isFav ? 'DELETE' : 'POST';
|
||||
fetch(`/board/api/favorites/${dbId}`, { method, headers: { 'X-CSRF-Token': csrf } })
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (isFav) self.favoriteIds.delete(pageId);
|
||||
else self.favoriteIds.add(pageId);
|
||||
self.contextMenu.visible = false;
|
||||
// Reload to update sidebar
|
||||
location.reload();
|
||||
})
|
||||
.catch(e => alert('Favorite action failed'));
|
||||
break;
|
||||
case 'copyLink':
|
||||
const url = window.location.origin + '/' + pageId;
|
||||
|
||||
Reference in New Issue
Block a user