fix: side peek des bases repasse en vanilla JS + largeur 1100px standard (v7.49.0)
FlowDeck CI / test (push) Failing after 3h13m58s
FlowDeck CI / lint (push) Successful in 2m12s
FlowDeck CI / docker (push) Skipped

- Panneau peek: les bindings Alpine (x-data absent du conteneur) rendaient
  loovverture et le redimensionnement inoperants -> cblage direct sur le document.
- Helper unique window.fdWirePeekResize (app.js): pointer capture, 300px-90vw,
  clic=fermer, largeur persiste fd_peek_width partagee entre les 4 peeks.
- database-table-container margin:0 (tableau colle a gauche, marge Library).
- .lib-container remonte dans app.css (trash etait pleine largeur), .db-index 1100px.
- ObsiGate verifie sans code: creation .xlsx OK (openpyxl, #186).
This commit is contained in:
2026-10-05 22:47:36 -04:00
parent 894004a1f5
commit 1d1cdbd618
63 changed files with 9473 additions and 393 deletions
+91 -40
View File
@@ -238,6 +238,23 @@ const DB_CONFIG=(()=>{try{const el=document.getElementById('db-config');const v=
return '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">'+(VIEW_ICONS[t]||VIEW_ICONS.table)+'</svg>';
}
function taskDbButton(collection) {
// Le libellé suit l'état réel de la base : convertie, à
// configurer, ou pas encore convertie. Un simple ✓ était
// introuvable pour l'utilisateur.
var isTask = !!(collection && collection.is_task);
var mapped = !!(collection && collection.task_assignee_prop &&
collection.task_status_prop && collection.task_due_prop);
var label = isTask ? (mapped ? '✓ Base de tâches' : '⚠ Configurer')
: 'Convertir en base de tâches';
var title = isTask
? (mapped ? 'Cette base alimente My Tasks'
: 'Base connectée mais mapping incomplet — reenregistrer')
: 'Relier Assigné à / Statut / Échéance pour alimenter My Tasks';
return '<button class="db-task-db-btn" data-task-db="' + collectionId +
'" title="' + escHtml(title) + '">' + label + '</button>';
}
function renderViewBar() {
var h = '<div class="db-view-bar" style="padding:4px 0 8px"><div class="db-view-tabs">';
if (!state.views.length) {
@@ -251,7 +268,8 @@ const DB_CONFIG=(()=>{try{const el=document.getElementById('db-config');const v=
});
h += '<button class="db-view-add" data-view-add="1" title="Add a view">+</button>';
h += '</div>';
h += '<div class="db-view-actions"><button class="db-btn-new db-add-page-btn">+ New</button></div>';
h += '<div class="db-view-actions">' + taskDbButton(state.collection) +
'<button class="db-btn-new db-add-page-btn">+ New</button></div>';
h += '</div>';
return h;
}
@@ -1264,51 +1282,84 @@ const DB_CONFIG=(()=>{try{const el=document.getElementById('db-config');const v=
}
}
// ── Global Side Peek for Database Pages (auto-creates panel) ──
function openPageInSidePeek(pageId) {
var panel = document.getElementById('db-side-peek');
if (!panel) {
panel = document.createElement('div');
panel.id = 'db-side-peek';
panel.style.cssText = 'position:fixed;top:0;right:0;width:560px;height:100vh;background:var(--bg-primary);box-shadow:-4px 0 20px rgba(0,0,0,0.12);z-index:900;display:flex;flex-direction:column';
panel.innerHTML = '<div id="db-side-peek-resize" style="position:absolute;top:0;left:0;width:6px;height:100%;cursor:col-resize;z-index:2"></div>'+
'<div style="display:flex;align-items:center;justify-content:space-between;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">'+
'<button id="db-side-peek-close" style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:6px;border:none;background:transparent;cursor:pointer;font-size:18px;color:var(--text-dim)">×</button>'+
'<button id="db-side-peek-full" style="display:flex;align-items:center;justify-content:center;width:28px;height:28px;border-radius:6px;border:none;background:transparent;cursor:pointer;font-size:18px;color:var(--text-dim)" title="Open full page">↗</button>'+
'</div>'+
'<iframe id="db-side-peek-iframe" src="" style="width:100%;height:100%;border:none;flex:1"></iframe>';
document.body.appendChild(panel);
document.getElementById('db-side-peek-close').onclick = function(){ panel.style.display='none'; document.getElementById('db-side-peek-iframe').src=''; };
document.getElementById('db-side-peek-full').onclick = function(){
var ifr = document.getElementById('db-side-peek-iframe');
if (ifr && ifr.src) window.open(ifr.src.replace('?embed=1',''),'_blank');
};
var rh = document.getElementById('db-side-peek-resize');
rh.onpointerdown = function(e) {
e.preventDefault(); e.stopPropagation();
var sx = e.clientX, sw = panel.offsetWidth;
function mv(ev) { var w = Math.max(300, Math.min(window.innerWidth*0.9, sw-(ev.clientX-sx))); panel.style.width = w+'px'; }
function up() { document.removeEventListener('pointermove',mv); document.removeEventListener('pointerup',up); document.body.style.cursor=''; }
document.body.style.cursor='col-resize';
document.addEventListener('pointermove',mv); document.addEventListener('pointerup',up);
};
// ── Side peek : panneau latéral des lignes de base (vanilla JS) ──
// Le panneau vit dans _database_table.html (page de base ET bases
// embarquées inline). Il doit être câblé SUR le document, jamais via
// container.__x : le conteneur n'a pas de x-data (les bindings Alpine
// du panneau restaient inertes — clic sur une ligne = rien).
var peekEl = document.getElementById('db-side-peek');
// Sans panneau (collections embarquées inline dans l'éditeur), le clic ne
// doit pas lever de ReferenceError : repli discret, comme l'ancien code.
if (!peekEl) window.openPageInSidePeek = function() {};
if (peekEl) {
var peekFrame = document.getElementById('db-side-peek-iframe');
var peekTitle = document.getElementById('db-side-peek-title');
var peekLoading = document.getElementById('db-side-peek-loading');
function closeDbPeek() {
peekEl.classList.remove('open');
document.getElementById('db-side-peek-iframe').src='about:blank';
if (peekTitle) { peekTitle.textContent = ''; }
}
var iframe = document.getElementById('db-side-peek-iframe');
// v6.5.0: a database row id is NOT a pages id (raw /pages/{rowId}
// opened an unrelated page with the same id) — resolve the row's
// content page first (lazy-created), then load the real editor.
resolveRowContentPage(pageId).then(function(pid){
var fr = document.getElementById('db-side-peek-iframe');
if (fr) { fr.src = '/pages/'+pid+'?embed=1'; panel.style.display = 'flex'; }
window.openPageInSidePeek = function(pageId) {
if (!pageId) return;
peekEl.classList.add('open');
if (peekLoading) peekLoading.style.display = '';
var iframe = document.getElementById('db-side-peek-iframe');
if (iframe) iframe.src='about:blank';
if (peekTitle) peekTitle.textContent = '';
resolveRowContentPage(pageId).then(function(pid){
if (!peekEl.classList.contains('open')) return;
if (peekTitle) peekTitle.textContent = '';
if (peekLoading) peekLoading.style.display = 'none';
if (iframe) iframe.src = '/pages/'+pid;
})
.catch(function(){
// L'échec est rendu DANS le panneau (srcdoc) : sous le build
// CSP d'Alpine les toasts ne s'affichent pas partout.
if (iframe) {
iframe.srcdoc = '<p style="font-family:sans-serif;color:#888;padding:40px;text-align:center">Impossible d’ouvrir le contenu de cette ligne</p>';
}
peekEl.classList.add('open');
toast('Impossible d’ouvrir le contenu de cette ligne', 'error');
});
};
var pc = document.getElementById('db-side-peek-close');
if (pc) pc.addEventListener('click', closeDbPeek);
var pf = document.getElementById('db-side-peek-full');
if (pf) pf.addEventListener('click', function() {
if (peekFrame && peekFrame.src && peekFrame.src !== 'about:blank') {
window.location.href = peekFrame.src;
}
});
if (iframe) panel.style.display = 'flex';
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape' && peekEl.classList.contains('open')) closeDbPeek();
});
document.addEventListener('pointerdown', function(e) {
if (!peekEl.classList.contains('open')) return;
if (peekEl.contains(e.target)) return;
if (e.target.closest('#db-table-container, .database-table-container, [id^="inline-db-"]')) return;
closeDbPeek();
});
function wireDbPeekResize() {
if (window.fdWirePeekResize) {
window.fdWirePeekResize(peekEl, peekEl.querySelector('.peek-resize-handle'), closeDbPeek);
} else if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', wireDbPeekResize);
}
}
wireDbPeekResize();
}
function resolveRowContentPage(rowId){
return fetch('/db/pages/'+rowId+'/open/api')
.then(function(r){ return r.ok ? r.json() : null; })
.then(function(d){ return (d && d.page_id) ? d.page_id : rowId; })
.catch(function(){ return rowId; });
.then(function(r){
if (!r.ok) throw new Error('HTTP ' + r.status);
return r.json();
})
.then(function(d){
if (!d || !d.page_id) throw new Error('page de contenu introuvable');
return d.page_id;
});
}
})();
}