Archives — panneau des fiches supprimées avec restauration

Bouton Archives dans la barre de filtres de la frise.
Panneau latéral listant les fiches supprimées avec aperçu complet.
Restauration recrée la fiche dans la frise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Etienne Delvarre
2026-06-03 15:34:27 +02:00
parent 8a88e6a8f0
commit 2c170f9ef2
2 changed files with 117 additions and 10 deletions
+104 -10
View File
@@ -604,6 +604,7 @@ window.dispatchEvent(new Event('tiptap-loaded'));
</select>
</div>
<button class="btn-clear-filters" onclick="clearFilters()">Effacer</button>
<button class="btn-history" onclick="openArchives()" style="margin-left:auto;">Archives</button>
</div>
<div class="frise-wrapper">
<div class="timeline-area" id="timeline-area">
@@ -3168,26 +3169,119 @@ function selectVersion(id) {
async function restoreVersion() {
if (!selectedVersionId) return;
if (!confirm('Restaurer cette version ? L\u2019état actuel sera sauvegardé avant la restauration.')) return;
const isArchive = versionType === 'archive';
const msg = isArchive
? 'Restaurer cette fiche supprimée ? Elle sera recréée dans la frise.'
: 'Restaurer cette version ? L\u2019état actuel sera sauvegardé avant la restauration.';
if (!confirm(msg)) return;
try {
const r = await fetch(`/api/versions/${selectedVersionId}/restore`, { method: 'POST' });
if (r.ok) {
closeVersionPanel();
if (versionType === 'frise') {
if (isArchive) {
// Re-create the card from snapshot
const v = versionList.find(x => x.Id === selectedVersionId);
if (!v) return;
const snap = JSON.parse(v.Snapshot);
const { Title, Acte, Ordre, Personnage, Tags, Notes } = snap;
const data = { Title, Acte: Acte || 'En attente', Ordre: Ordre || 999, Personnage, Tags, Notes, 'Résumé': snap['Résumé'], 'Intérêt narratif': snap['Intérêt narratif'], Implications: snap.Implications, Origine: snap.Origine, 'Lien rédaction': snap['Lien rédaction'] };
const r = await fetch('/api/records', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) });
if (r.ok) {
closeVersionPanel();
await loadData();
if (editingId) openEdit(editingId);
} else if (versionType === 'redaction' && currentSceneId) {
const scene = records.find(rec => rec.Id === currentSceneId);
if (scene) openScene(scene);
} else {
alert('Erreur lors de la restauration');
}
} else {
alert('Erreur lors de la restauration');
const r = await fetch(`/api/versions/${selectedVersionId}/restore`, { method: 'POST' });
if (r.ok) {
closeVersionPanel();
if (versionType === 'frise') {
await loadData();
if (editingId) openEdit(editingId);
} else if (versionType === 'redaction' && currentSceneId) {
const scene = records.find(rec => rec.Id === currentSceneId);
if (scene) openScene(scene);
}
} else {
alert('Erreur lors de la restauration');
}
}
} catch (e) {
alert('Erreur : ' + e.message);
}
}
// ── Archives (deleted cards) ──
async function openArchives() {
versionType = 'archive';
versionRecordId = null;
selectedVersionId = null;
document.getElementById('version-panel-title').textContent = 'Archives — Fiches supprimées';
document.getElementById('version-list').innerHTML = '<div style="padding:20px;color:var(--text-muted);text-align:center;">Chargement...</div>';
document.getElementById('version-preview').style.display = 'none';
document.getElementById('version-actions').style.display = 'none';
document.getElementById('version-panel').classList.add('open');
try {
const r = await fetch('/api/archives');
const data = await r.json();
versionList = data.list || [];
renderArchiveList();
} catch (e) {
document.getElementById('version-list').innerHTML = '<div style="padding:20px;color:var(--text-muted);text-align:center;">Erreur de chargement</div>';
}
}
function renderArchiveList() {
const container = document.getElementById('version-list');
if (versionList.length === 0) {
container.innerHTML = '<div style="padding:20px;color:var(--text-muted);text-align:center;font-style:italic;">Aucune fiche supprimée.</div>';
return;
}
container.innerHTML = '';
for (const v of versionList) {
const div = document.createElement('div');
div.className = 'version-item';
div.dataset.id = v.Id;
const date = new Date(v.CreatedAt);
const dateStr = date.toLocaleDateString('fr-FR', { day: 'numeric', month: 'long', year: 'numeric', hour: '2-digit', minute: '2-digit' });
let title = '', preview = '';
try {
const snap = JSON.parse(v.Snapshot);
title = snap.Title || snap.Titre || 'Sans titre';
preview = [snap.Acte, snap['Résumé']].filter(Boolean).join(' — ');
} catch (e) { title = 'Fiche'; }
div.innerHTML = `<div class="version-item-date">${dateStr}</div>` +
`<div class="version-item-label">${title.replace(/</g, '&lt;')}</div>` +
(preview ? `<div class="version-item-preview">${preview.replace(/</g, '&lt;').substring(0, 200)}</div>` : '');
div.onclick = () => selectArchive(v.Id);
container.appendChild(div);
}
}
function selectArchive(id) {
selectedVersionId = id;
versionType = 'archive';
document.querySelectorAll('.version-item').forEach(el => el.classList.toggle('selected', Number(el.dataset.id) === id));
const v = versionList.find(x => x.Id === id);
if (!v) return;
try {
const snap = JSON.parse(v.Snapshot);
const content = document.getElementById('version-preview-content');
const parts = [];
if (snap.Title) parts.push(`<strong>${snap.Title.replace(/</g, '&lt;')}</strong>`);
if (snap.Acte) parts.push(`<em>${snap.Acte}</em>`);
if (snap.Personnage) parts.push('Personnages : ' + snap.Personnage.replace(/</g, '&lt;'));
if (snap['Résumé']) parts.push(snap['Résumé'].replace(/</g, '&lt;'));
if (snap['Intérêt narratif']) parts.push('<strong>Intérêt :</strong> ' + snap['Intérêt narratif'].replace(/</g, '&lt;'));
if (snap.Tags) parts.push('<em>Tags : ' + snap.Tags.replace(/</g, '&lt;') + '</em>');
if (snap.Notes) parts.push('<strong>Notes :</strong> ' + snap.Notes.replace(/</g, '&lt;'));
content.innerHTML = parts.join('<br>');
document.getElementById('version-preview').style.display = '';
document.getElementById('version-actions').style.display = '';
} catch (e) {
document.getElementById('version-preview').style.display = 'none';
}
}
// Close popup on version panel click outside
document.addEventListener('click', (e) => {
const panel = document.getElementById('version-panel');