fix: bouton delete cassé + preview PDF cliquable + icônes distinctes FlowDeck vs upload
FlowDeck CI / test (push) Failing after 8s
FlowDeck CI / docker (push) Has been skipped

1. Delete: ev.target pouvait être un noeud texte (emoji 🗑)
   → Text.closest() n'existe pas → handleTreeAction échouait.
   Fix: fallback sur ev.target.parentElement.closest().

2. Preview PDF: ajout lien 'Open viewer →' vers /pages/{id}
   (le viewer standalone rend le PDF dans une iframe).

3. Icônes distinctes:
   - Pages FlowDeck (content_format='blocks'/'markdown') → 📝
   - Fichiers uploadés → icône basée sur l'extension
   - _fileIcon() accepte contentFormat, propage depuis l'API tree
   - Appliqué dans sidebar (board.py) + page workspace (x-for + renderChildren)
This commit is contained in:
2026-07-11 14:00:57 -04:00
parent fc6e30aebb
commit 8497a36da9
2 changed files with 16 additions and 10 deletions
+8 -5
View File
@@ -207,7 +207,7 @@
<span style="width:16px;flex-shrink:0;"></span>
</template>
<span class="icon" x-text="_fileIcon(node.name, node.is_folder)"></span>
<span class="icon" x-text="_fileIcon(node.name, node.is_folder, node.content_format)"></span>
<template x-if="node.is_folder">
<span class="name folder" @click.stop="navigateToFolder(node.id)" x-text="node.name"></span>
@@ -376,7 +376,7 @@ document.addEventListener('alpine:init', function() {
for (var i=0; i<children.length; i++) {
var c = children[i];
var hasKids = c.children && c.children.length > 0;
var icon = this._fileIcon(c.name, c.is_folder);
var icon = this._fileIcon(c.name, c.is_folder, c.content_format);
var escName = this.esc(c.name);
html += '<li><div class="ws-tree-item" style="padding-left:'+(depth*20)+'px" draggable="true"'
+ ' data-folder-id="'+(c.is_folder?c.id:'')+'"'
@@ -421,7 +421,8 @@ document.addEventListener('alpine:init', function() {
// ── Event delegation for renderChildren action buttons ──
handleTreeAction(ev) {
var btn = ev.target.closest('[data-ws-action]');
var btn = (ev.target.closest && ev.target.closest('[data-ws-action]'))
|| (ev.target.parentElement && ev.target.parentElement.closest('[data-ws-action]'));
if (!btn) return;
ev.stopPropagation();
var action = btn.getAttribute('data-ws-action');
@@ -516,8 +517,10 @@ document.addEventListener('alpine:init', function() {
},
// ── File type icon ──
_fileIcon(name, isFolder) {
_fileIcon(name, isFolder, contentFormat) {
if (isFolder) return '📁';
// FlowDeck internal pages (no extension, blocks/markdown format)
if (contentFormat && contentFormat !== 'file') return '📝';
var n = (name || '').toLowerCase();
if (/\.(png|jpe?g|gif|webp|svg|bmp|ico)$/.test(n)) return '🖼️';
if (/\.pdf$/.test(n)) return '📕';
@@ -598,7 +601,7 @@ document.addEventListener('alpine:init', function() {
if (isImg) {
this.previewContent = '<img src="'+url+'" style="max-width:380px;max-height:180px;display:block;">';
} else if ((node.name||'').toLowerCase().endsWith('.pdf')) {
this.previewContent = '<div style="text-align:center;padding:16px;"><span style="font-size:48px;">📕</span><br><span style="color:var(--text-secondary);font-size:12px;">PDF Document</span></div>';
this.previewContent = '<div style="text-align:center;padding:16px;"><span style="font-size:48px;">📕</span><br><span style="color:var(--text-secondary);font-size:12px;">PDF Document</span><br><a href="/pages/'+node.id+'" style="color:var(--accent);font-size:11px;margin-top:4px;display:inline-block;">Open viewer →</a></div>';
} else {
this.previewContent = 'Loading...';
fetch(url)