Coalesce index generation increments and add rebuild cooldown
This commit is contained in:
+102
-57
@@ -3380,34 +3380,43 @@
|
||||
// ── Dashboard Conflicts Widget ──
|
||||
const DashboardConflictsWidget = {
|
||||
async load() {
|
||||
const section = document.getElementById("dashboard-conflicts-section");
|
||||
if (!section) return;
|
||||
const container = document.getElementById("dashboard-conflicts-container");
|
||||
if (!container) return;
|
||||
try {
|
||||
const data = await api("/api/conflicts");
|
||||
if (data.total === 0) { section.style.display = "none"; return; }
|
||||
section.style.display = "";
|
||||
document.getElementById("dashboard-conflicts-count").textContent = data.total;
|
||||
this.render(data.conflicts);
|
||||
} catch (err) { section.style.display = "none"; }
|
||||
if (data.total === 0) { container.innerHTML = ""; return; }
|
||||
this.render(data.conflicts, container);
|
||||
} catch (err) { container.innerHTML = ""; }
|
||||
},
|
||||
render(conflicts) {
|
||||
const grid = document.getElementById("dashboard-conflicts-grid");
|
||||
if (!grid) return;
|
||||
grid.innerHTML = conflicts.map(c => `
|
||||
<div class="conflict-card">
|
||||
<div class="conflict-info">
|
||||
<span class="conflict-vault">${escapeHtml(c.vault)}</span>
|
||||
<span class="conflict-name">${escapeHtml(c.conflict_path.split("/").pop())}</span>
|
||||
<span class="conflict-date">Conflit du ${c.conflict_date.replace(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/, "$3/$2/$1 $4:$5")}</span>
|
||||
render(conflicts, container) {
|
||||
container.innerHTML = `
|
||||
<div class="dashboard-section">
|
||||
<div class="dashboard-header">
|
||||
<div class="dashboard-title-row">
|
||||
<i data-lucide="alert-triangle" class="dashboard-icon" style="color:var(--accent-orange)"></i>
|
||||
<h2>Conflits de synchronisation</h2>
|
||||
<span class="dashboard-badge" style="background:var(--accent-orange)">${conflicts.length}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="conflict-actions">
|
||||
<button class="conflict-btn keep-local" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder l'original</button>
|
||||
<button class="conflict-btn keep-conflict" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder le conflit</button>
|
||||
<div class="dashboard-conflicts-grid">
|
||||
${conflicts.map(c => `
|
||||
<div class="conflict-card">
|
||||
<div class="conflict-info">
|
||||
<span class="conflict-vault">${escapeHtml(c.vault)}</span>
|
||||
<span class="conflict-name">${escapeHtml(c.conflict_path.split("/").pop())}</span>
|
||||
<span class="conflict-date">Conflit du ${c.conflict_date.replace(/(\d{4})(\d{2})(\d{2})-(\d{2})(\d{2})/, "$3/$2/$1 $4:$5")}</span>
|
||||
</div>
|
||||
<div class="conflict-actions">
|
||||
<button class="conflict-btn keep-local" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder l'original</button>
|
||||
<button class="conflict-btn keep-conflict" data-vault="${escapeHtml(c.vault)}" data-conflict="${escapeHtml(c.conflict_path)}" data-original="${escapeHtml(c.original_path)}">Garder le conflit</button>
|
||||
</div>
|
||||
</div>
|
||||
`).join("")}
|
||||
</div>
|
||||
</div>
|
||||
`).join("");
|
||||
grid.querySelectorAll(".keep-local").forEach(btn => btn.addEventListener("click", () => this._resolve(btn.dataset, "keep_local")));
|
||||
grid.querySelectorAll(".keep-conflict").forEach(btn => btn.addEventListener("click", () => this._resolve(btn.dataset, "keep_conflict")));
|
||||
</div>`;
|
||||
lucide.createIcons();
|
||||
container.querySelectorAll(".keep-local").forEach(btn => btn.addEventListener("click", () => this._resolve(btn.dataset, "keep_local")));
|
||||
container.querySelectorAll(".keep-conflict").forEach(btn => btn.addEventListener("click", () => this._resolve(btn.dataset, "keep_conflict")));
|
||||
},
|
||||
async _resolve(d, action) {
|
||||
try {
|
||||
@@ -5584,23 +5593,37 @@
|
||||
|
||||
function showWelcome() {
|
||||
hideProgressBar();
|
||||
|
||||
// Ensure the dashboard container exists and has the correct structure (it might have been wiped by renderFile or be an old version)
|
||||
|
||||
// Restore or rebuild the dashboard with tabbed sections
|
||||
const area = document.getElementById("content-area");
|
||||
const home = document.getElementById("dashboard-home");
|
||||
const bookmarksSection = document.getElementById("dashboard-bookmarks-section");
|
||||
|
||||
if (area && (!home || !bookmarksSection)) {
|
||||
|
||||
if (area && !home) {
|
||||
area.innerHTML = `
|
||||
<div id="dashboard-home" class="dashboard-home" role="region" aria-label="Tableau de bord">
|
||||
<!-- Bookmarks Section -->
|
||||
<div id="dashboard-bookmarks-section" class="dashboard-section">
|
||||
<div class="dashboard-header">
|
||||
<div class="dashboard-title-row">
|
||||
<i data-lucide="bookmark" class="dashboard-icon" style="color:var(--accent-green)"></i>
|
||||
<h2>Bookmarks</h2>
|
||||
</div>
|
||||
<!-- Dashboard Tabs -->
|
||||
<div class="dashboard-tabs">
|
||||
<button class="dashboard-tab active" data-tab="stats">
|
||||
<i data-lucide="bar-chart-3" style="width:14px;height:14px"></i> Statistiques
|
||||
</button>
|
||||
<button class="dashboard-tab" data-tab="bookmarks">
|
||||
<i data-lucide="bookmark" style="width:14px;height:14px"></i> Bookmarks
|
||||
</button>
|
||||
<button class="dashboard-tab" data-tab="recent">
|
||||
<i data-lucide="clock" style="width:14px;height:14px"></i> Récents
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Stats Panel -->
|
||||
<div id="dashboard-panel-stats" class="dashboard-panel active">
|
||||
<div id="dashboard-stats-grid" class="dashboard-stats-grid">
|
||||
<div class="dashboard-stats-loading">Chargement...</div>
|
||||
</div>
|
||||
<div id="dashboard-conflicts-container" style="margin-top:16px"></div>
|
||||
</div>
|
||||
|
||||
<!-- Bookmarks Panel -->
|
||||
<div id="dashboard-panel-bookmarks" class="dashboard-panel">
|
||||
<div id="dashboard-bookmarks-grid" class="dashboard-recent-grid"></div>
|
||||
<div id="dashboard-bookmarks-empty" class="dashboard-recent-empty">
|
||||
<i data-lucide="pin"></i>
|
||||
@@ -5609,12 +5632,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recently Opened Section -->
|
||||
<div id="dashboard-recent-section" class="dashboard-section">
|
||||
<!-- Recent Panel -->
|
||||
<div id="dashboard-panel-recent" class="dashboard-panel">
|
||||
<div class="dashboard-header">
|
||||
<div class="dashboard-title-row">
|
||||
<i data-lucide="clock" class="dashboard-icon"></i>
|
||||
<h2>Derniers fichiers ouverts</h2>
|
||||
<span id="dashboard-count" class="dashboard-badge"></span>
|
||||
</div>
|
||||
<div class="dashboard-actions">
|
||||
@@ -5623,18 +5644,11 @@
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-recent-grid" class="dashboard-recent-grid"></div>
|
||||
|
||||
<div id="dashboard-loading" class="dashboard-loading">
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div><div class="skeleton-card"></div><div class="skeleton-card"></div>
|
||||
<div class="skeleton-card"></div><div class="skeleton-card"></div><div class="skeleton-card"></div>
|
||||
</div>
|
||||
|
||||
<div id="dashboard-recent-empty" class="dashboard-recent-empty hidden">
|
||||
<i data-lucide="inbox"></i>
|
||||
<span>Aucun fichier récent</span>
|
||||
@@ -5642,15 +5656,36 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
|
||||
// Re-initialize widgets which might need to bind events to new elements
|
||||
|
||||
// Wire dashboard tab switching
|
||||
document.querySelectorAll(".dashboard-tab").forEach(tab => {
|
||||
tab.addEventListener("click", function() {
|
||||
document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active"));
|
||||
document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active"));
|
||||
this.classList.add("active");
|
||||
const panel = document.getElementById("dashboard-panel-" + this.dataset.tab);
|
||||
if (panel) panel.classList.add("active");
|
||||
});
|
||||
});
|
||||
|
||||
// Re-initialize widgets
|
||||
if (typeof DashboardRecentWidget !== "undefined") {
|
||||
DashboardRecentWidget.init();
|
||||
}
|
||||
safeCreateIcons();
|
||||
} else if (home) {
|
||||
// Dashboard already exists, just show it
|
||||
home.style.display = "";
|
||||
// Activate default tab
|
||||
document.querySelectorAll(".dashboard-tab").forEach(t => t.classList.remove("active"));
|
||||
document.querySelectorAll(".dashboard-panel").forEach(p => p.classList.remove("active"));
|
||||
const defaultTab = document.querySelector('.dashboard-tab[data-tab="stats"]');
|
||||
const defaultPanel = document.getElementById("dashboard-panel-stats");
|
||||
if (defaultTab) defaultTab.classList.add("active");
|
||||
if (defaultPanel) defaultPanel.classList.add("active");
|
||||
}
|
||||
|
||||
// Show the dashboard widgets
|
||||
// Load all widgets (they handle missing elements gracefully)
|
||||
if (typeof DashboardStatsWidget !== "undefined") {
|
||||
DashboardStatsWidget.load();
|
||||
}
|
||||
@@ -7763,12 +7798,22 @@
|
||||
|
||||
_showDashboard() {
|
||||
const area = document.getElementById("content-area");
|
||||
area.innerHTML = "";
|
||||
const dashboard = document.getElementById("dashboard-home");
|
||||
if (dashboard) {
|
||||
dashboard.style.display = "";
|
||||
area.appendChild(dashboard);
|
||||
// Save dashboard DOM before clearing (it may have been removed from DOM by renderFile)
|
||||
let dashboard = document.getElementById("dashboard-home");
|
||||
if (!dashboard) {
|
||||
// Dashboard was destroyed — rebuild via showWelcome
|
||||
area.innerHTML = "";
|
||||
showWelcome();
|
||||
return;
|
||||
}
|
||||
area.innerHTML = "";
|
||||
dashboard.style.display = "";
|
||||
area.appendChild(dashboard);
|
||||
// Refresh widgets after restoring
|
||||
if (typeof DashboardStatsWidget !== "undefined") DashboardStatsWidget.load();
|
||||
if (typeof DashboardConflictsWidget !== "undefined") DashboardConflictsWidget.load();
|
||||
if (typeof DashboardRecentWidget !== "undefined") DashboardRecentWidget.load(selectedContextVault);
|
||||
if (typeof DashboardBookmarkWidget !== "undefined") DashboardBookmarkWidget.load(selectedContextVault);
|
||||
if (history.pushState) {
|
||||
history.pushState(null, "", "#");
|
||||
}
|
||||
@@ -7789,7 +7834,7 @@
|
||||
|
||||
this._tabs.forEach((tab, idx) => {
|
||||
const el = document.createElement("div");
|
||||
el.className = "tab-item" + (tab.id === this._activeTabId ? " active" : "");
|
||||
el.className = "tab-item" + (tab.id === this._activeTabId ? " active" : "") + (tab.preview ? " preview" : "");
|
||||
el.draggable = true;
|
||||
el.dataset.tabId = tab.id;
|
||||
el.dataset.index = idx;
|
||||
|
||||
@@ -5744,3 +5744,37 @@ body.popup-mode .content-area {
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
/* ── Dashboard Tab Navigation ── */
|
||||
.dashboard-tabs {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
margin-bottom: 16px;
|
||||
border-bottom: 2px solid var(--border);
|
||||
}
|
||||
.dashboard-tab {
|
||||
padding: 8px 16px;
|
||||
border: none;
|
||||
background: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
border-bottom: 2px solid transparent;
|
||||
margin-bottom: -2px;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
}
|
||||
.dashboard-tab:hover { color: var(--text-primary); }
|
||||
.dashboard-tab.active {
|
||||
color: var(--accent);
|
||||
border-bottom-color: var(--accent);
|
||||
}
|
||||
.dashboard-panel {
|
||||
display: none;
|
||||
}
|
||||
.dashboard-panel.active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user