- Suppression déplace dans .trash/ (arborescence préservée + .trashinfo) - /trash : liste, restauration (conflit overwrite/rename), suppression définitive, vidage complet - Purge automatique des fichiers > 30 jours à chaque visite /trash - Badge rouge dans la nav avec le nombre de fichiers en corbeille - Extraction du tableau de fichiers en partial _file_table.html partagé entre browse et trash Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
92 lines
3.3 KiB
HTML
92 lines
3.3 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Corbeille{% endblock %}
|
|
|
|
{% block content %}
|
|
|
|
<section class="card">
|
|
<div class="trash-header">
|
|
<h2>Corbeille
|
|
{% if entries %}
|
|
<span class="trash-count-label">— {{ entries|length }} fichier{{ 's' if entries|length > 1 else '' }}</span>
|
|
{% endif %}
|
|
</h2>
|
|
{% if entries %}
|
|
<form method="post" action="{{ url_for('trash_empty') }}"
|
|
onsubmit="return confirm('Vider définitivement toute la corbeille ?')">
|
|
<button type="submit" class="btn btn-danger">Vider la corbeille</button>
|
|
</form>
|
|
{% endif %}
|
|
</div>
|
|
<p class="trash-info">Les fichiers sont supprimés définitivement après 30 jours.</p>
|
|
|
|
{% if entries %}
|
|
{% set mode = 'trash' %}
|
|
{% include '_file_table.html' %}
|
|
{% else %}
|
|
<p class="empty">La corbeille est vide.</p>
|
|
{% endif %}
|
|
</section>
|
|
|
|
<div id="restore-conflict-panel" class="conflict-panel" style="display:none">
|
|
<p class="conflict-title">⚠ Un fichier existe déjà à cet emplacement : <strong id="restore-conflict-name"></strong></p>
|
|
<div class="resize-chips resize-chips--radio">
|
|
<label class="chip"><input type="radio" name="restore-conflict" value="overwrite" checked><span>Écraser</span></label>
|
|
<label class="chip"><input type="radio" name="restore-conflict" value="rename"><span>Renommer</span></label>
|
|
</div>
|
|
<div class="conflict-actions">
|
|
<button type="button" id="restore-confirm" class="btn btn-primary">Confirmer la restauration</button>
|
|
<button type="button" id="restore-cancel" class="btn-rename-cancel">Annuler</button>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const RESTORE_URL = {{ url_for('trash_restore') | tojson }};
|
|
const conflictPanel = document.getElementById('restore-conflict-panel');
|
|
const conflictName = document.getElementById('restore-conflict-name');
|
|
const confirmBtn = document.getElementById('restore-confirm');
|
|
const cancelBtn = document.getElementById('restore-cancel');
|
|
let pendingPath = null;
|
|
|
|
async function doRestore(path, conflict) {
|
|
const fd = new FormData();
|
|
fd.append('path', path);
|
|
if (conflict) fd.append('conflict', conflict);
|
|
try {
|
|
const resp = await fetch(RESTORE_URL, { method: 'POST', body: fd });
|
|
if (resp.status === 409) {
|
|
pendingPath = path;
|
|
conflictName.textContent = path.split('/').pop();
|
|
conflictPanel.style.display = 'block';
|
|
conflictPanel.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
return;
|
|
}
|
|
if (resp.redirected) { window.location.href = resp.url; return; }
|
|
if (resp.ok) { window.location.href = resp.url || window.location.href; return; }
|
|
alert('Erreur lors de la restauration.');
|
|
} catch (_) {
|
|
alert('Erreur réseau.');
|
|
}
|
|
}
|
|
|
|
document.addEventListener('click', e => {
|
|
const btn = e.target.closest('.btn-restore');
|
|
if (!btn) return;
|
|
doRestore(btn.dataset.path, '');
|
|
});
|
|
|
|
confirmBtn.addEventListener('click', () => {
|
|
const strategy = document.querySelector('input[name="restore-conflict"]:checked')?.value || 'overwrite';
|
|
conflictPanel.style.display = 'none';
|
|
if (pendingPath) doRestore(pendingPath, strategy);
|
|
pendingPath = null;
|
|
});
|
|
|
|
cancelBtn.addEventListener('click', () => {
|
|
conflictPanel.style.display = 'none';
|
|
pendingPath = null;
|
|
});
|
|
})();
|
|
</script>
|
|
|
|
{% endblock %}
|