feat: inspecteur droit pour le tableau de bord XLSX #154
CI / lint (push) Successful in 2m30s
CI / security (push) Failing after 1m46s
CI / build (push) Canceled after 0s
CI / e2e (push) Canceled after 0s
CI / test (push) Canceled after 1h32m51s

This commit is contained in:
2026-09-29 14:56:08 -04:00
parent 8da65611cb
commit e94af0369b
16 changed files with 183 additions and 31 deletions
+37 -4
View File
@@ -15,6 +15,7 @@ import { cacheViewedFile, getCachedFile } from './offline.js';
import { t } from './i18n.js';
import { onFileRender } from './plugins.js';
import { NowPlaying } from './now-playing.js';
import { openAssistant } from './ai-fab.js';
// ── Multi-format export ────────────────────────────────────────────────────
// Downloads a file export (HTML / MD bundle / ePub) via the authenticated
@@ -1198,7 +1199,23 @@ export function renderXlsxViewer(area, data) {
</button>
</span>
</div>
<div class="xlsx-panels">${panels}</div>
<div class="xlsx-body">
<div class="xlsx-main">
<div class="xlsx-panels">${panels}</div>
</div>
<aside class="xlsx-inspector" id="xlsx-inspector" aria-hidden="true">
<div class="xlsx-inspector-head">
<span class="xlsx-inspector-title">${escapeHtml(t("xlsx.inspector_title"))}</span>
<span class="xlsx-inspector-actions">
<button class="btn-action xlsx-inspector-ai" id="xlsx-inspector-ai" type="button" title="${escapeHtml(t("xlsx.inspector_ai"))}">
<i data-lucide="bot" style="width:14px;height:14px"></i> ${escapeHtml(t("xlsx.inspector_ai_short"))}
</button>
<button class="btn-action xlsx-inspector-close" id="xlsx-inspector-close" type="button" aria-label="${escapeHtml(t("common.cancel"))}">✕</button>
</span>
</div>
<div class="xlsx-inspector-body" id="xlsx-inspector-body"></div>
</aside>
</div>
</div>`;
// ── #153 A17 — dashboard panel: named ranges, objects, per-sheet KPI ──
@@ -1212,14 +1229,26 @@ export function renderXlsxViewer(area, data) {
title: t("xlsx.dashboard_btn"),
}, [icon("layout-dashboard", 14), document.createTextNode(t("xlsx.dashboard_btn"))]);
(area.querySelector("#xlsx-view-group") || area.querySelector(".xlsx-toolbar-actions")).appendChild(dashBtn);
const inspector = area.querySelector("#xlsx-inspector");
const inspectorBody = area.querySelector("#xlsx-inspector-body");
const closeInspector = () => {
const panel = inspectorBody.querySelector(".xlsx-dashboard");
if (panel) panel.remove();
inspector.classList.remove("open");
inspector.setAttribute("aria-hidden", "true");
dashBtn.classList.remove("active");
};
dashBtn.addEventListener("click", async () => {
const old = area.querySelector(".xlsx-dashboard");
if (old) { old.remove(); dashBtn.classList.remove("active"); return; }
// #154-A3 — the dashboard lives in a right-hand inspector so the grid
// stays visible beside it (it used to be prepended into the grid flow).
if (inspectorBody.querySelector(".xlsx-dashboard")) { closeInspector(); return; }
dashBtn.classList.add("active");
inspector.classList.add("open");
inspector.setAttribute("aria-hidden", "false");
const panel = document.createElement("div");
panel.className = "xlsx-dashboard";
panel.innerHTML = `<div class="xlsx-dashboard-loading">…</div>`;
area.querySelector(".xlsx-panels").prepend(panel);
inspectorBody.prepend(panel);
try {
const dash = await api(
`/api/file/${encodeURIComponent(data.vault)}/xlsx/dashboard?path=${encodeURIComponent(data.path)}`,
@@ -1261,6 +1290,10 @@ export function renderXlsxViewer(area, data) {
panel.innerHTML = `<p class="xlsx-kpi-empty">${escapeHtml(t("xlsx.load_error"))}: ${escapeHtml(String(err.message || err))}</p>`;
}
});
area.querySelector("#xlsx-inspector-close").addEventListener("click", closeInspector);
// #154-A3 — the assistant is the existing global side panel; the inspector
// just gives it a contextual entry point from the spreadsheet.
area.querySelector("#xlsx-inspector-ai").addEventListener("click", () => { openAssistant(); });
}
const saveBtn = area.querySelector("#xlsx-save-btn");
+3
View File
@@ -1877,6 +1877,9 @@
"xlsx.structure_error": "Could not change the structure",
"xlsx.last_sheet": "The last sheet cannot be deleted",
"xlsx.dashboard_btn": "Dashboard",
"xlsx.inspector_title": "Inspector",
"xlsx.inspector_ai": "AI assistant",
"xlsx.inspector_ai_short": "AI",
"xlsx.dashboard_title": "Workbook dashboard",
"xlsx.dashboard_stats": "{{cells}} cells · {{rows}} rows · {{cols}} columns · {{formulas}} formulas · {{numeric}} numeric values",
"xlsx.dashboard_charts": "{{n}} chart(s)",
+3
View File
@@ -1877,6 +1877,9 @@
"xlsx.structure_error": "Modification de la structure impossible",
"xlsx.last_sheet": "Impossible de supprimer la dernière feuille",
"xlsx.dashboard_btn": "Tableau de bord",
"xlsx.inspector_title": "Inspecteur",
"xlsx.inspector_ai": "Assistant IA",
"xlsx.inspector_ai_short": "IA",
"xlsx.dashboard_title": "Tableau de bord du classeur",
"xlsx.dashboard_stats": "{{cells}} cellules · {{rows}} lignes · {{cols}} colonnes · {{formulas}} formules · {{numeric}} valeurs numériques",
"xlsx.dashboard_charts": "{{n}} graphique(s)",
+65
View File
@@ -11289,6 +11289,71 @@ body.desktop-mode .editor-container {
color: var(--text-primary);
}
/* #154-A3 — right-hand inspector (dashboard, assistant) beside the grid. */
.xlsx-body {
display: flex;
align-items: stretch;
gap: 10px;
}
.xlsx-main {
flex: 1 1 auto;
min-width: 0;
}
.xlsx-inspector {
flex: 0 0 340px;
width: 340px;
max-width: 45%;
display: none;
flex-direction: column;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
max-height: 70vh;
}
.xlsx-inspector.open {
display: flex;
}
.xlsx-inspector-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
padding: 8px 10px;
border-bottom: 1px solid var(--border);
}
.xlsx-inspector-title {
font-weight: 600;
font-size: 0.85rem;
color: var(--text-primary);
}
.xlsx-inspector-actions {
display: inline-flex;
align-items: center;
gap: 6px;
}
.xlsx-inspector-body {
flex: 1 1 auto;
overflow: auto;
padding: 10px;
}
.xlsx-inspector .xlsx-dashboard {
margin: 0;
padding: 0;
border: none;
border-radius: 0;
background: transparent;
}
@media (max-width: 900px) {
.xlsx-body {
flex-direction: column;
}
.xlsx-inspector {
flex-basis: auto;
width: auto;
max-width: none;
}
}
/* #153 A17 — workbook dashboard panel: named ranges table + KPI cards.
Colors come from the existing CSS variables (no hardcoded values). */
.xlsx-dashboard {