feat: stats corbeille dans le tableau de bord
Nouvelle stat box cliquable (→ /trash) affichant le nombre de fichiers, la taille totale et la date du plus ancien fichier en corbeille. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b3af420d36
commit
d2683a02e0
3 changed files with 34 additions and 0 deletions
20
app/app.py
20
app/app.py
|
|
@ -279,6 +279,25 @@ def _trash_count() -> int:
|
||||||
if f.is_file() and not f.name.endswith(".trashinfo"))
|
if f.is_file() and not f.name.endswith(".trashinfo"))
|
||||||
|
|
||||||
|
|
||||||
|
def _trash_stats() -> dict:
|
||||||
|
if not TRASH_ROOT.exists():
|
||||||
|
return {"files": 0, "size": 0, "oldest": None}
|
||||||
|
files, size, oldest = 0, 0, None
|
||||||
|
for f in TRASH_ROOT.rglob("*"):
|
||||||
|
if not f.is_file() or f.name.endswith(".trashinfo"):
|
||||||
|
continue
|
||||||
|
files += 1
|
||||||
|
size += f.stat().st_size
|
||||||
|
info = Path(str(f) + ".trashinfo")
|
||||||
|
try:
|
||||||
|
dt = datetime.fromisoformat(json.loads(info.read_text())["deleted_at"])
|
||||||
|
if oldest is None or dt < oldest:
|
||||||
|
oldest = dt
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return {"files": files, "size": size, "oldest": oldest}
|
||||||
|
|
||||||
|
|
||||||
def _folder_stats(path: Path) -> dict:
|
def _folder_stats(path: Path) -> dict:
|
||||||
files, size = 0, 0
|
files, size = 0, 0
|
||||||
for f in path.rglob("*"):
|
for f in path.rglob("*"):
|
||||||
|
|
@ -399,6 +418,7 @@ def dashboard():
|
||||||
folders=folders,
|
folders=folders,
|
||||||
total_files=sum(v["files"] for v in folders.values()),
|
total_files=sum(v["files"] for v in folders.values()),
|
||||||
total_size=sum(v["size"] for v in folders.values()),
|
total_size=sum(v["size"] for v in folders.values()),
|
||||||
|
trash=_trash_stats(),
|
||||||
humansize=_humansize,
|
humansize=_humansize,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,11 @@ main { max-width: 1100px; margin: 2rem auto; padding: 0 1.5rem 3rem; display: fl
|
||||||
.stat-box { flex: 1; min-width: 160px; background: var(--blue-light); border-radius: 8px; padding: 1.2rem 1.5rem; }
|
.stat-box { flex: 1; min-width: 160px; background: var(--blue-light); border-radius: 8px; padding: 1.2rem 1.5rem; }
|
||||||
.stat-box .label { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: .4rem; }
|
.stat-box .label { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .05em; margin-bottom: .4rem; }
|
||||||
.stat-box .value { font-size: 1.9rem; font-weight: 700; color: var(--blue-dark); line-height: 1; }
|
.stat-box .value { font-size: 1.9rem; font-weight: 700; color: var(--blue-dark); line-height: 1; }
|
||||||
|
.stat-box .stat-sub { font-size: .75rem; color: var(--muted); margin-top: .35rem; }
|
||||||
|
.stat-box--trash { background: #fef2f2; text-decoration: none; transition: box-shadow .15s; cursor: pointer; }
|
||||||
|
.stat-box--trash:hover { box-shadow: 0 4px 16px rgba(239,68,68,.18); text-decoration: none; }
|
||||||
|
.stat-box--trash .label { color: #b91c1c; }
|
||||||
|
.stat-box--trash .value { color: #991b1b; }
|
||||||
|
|
||||||
/* ── Grille de dossiers ───────────────────────────────────────── */
|
/* ── Grille de dossiers ───────────────────────────────────────── */
|
||||||
.folder-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 1rem; }
|
.folder-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 1rem; }
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,15 @@
|
||||||
<div class="label">Dossiers</div>
|
<div class="label">Dossiers</div>
|
||||||
<div class="value">{{ folders | length }}</div>
|
<div class="value">{{ folders | length }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
<a href="{{ url_for('trash_list') }}" class="stat-box stat-box--trash">
|
||||||
|
<div class="label">Corbeille</div>
|
||||||
|
<div class="value">{{ trash.files }}</div>
|
||||||
|
<div class="stat-sub">{{ humansize(trash.size) }}
|
||||||
|
{% if trash.oldest %}
|
||||||
|
· depuis le {{ trash.oldest.strftime('%d/%m/%Y') }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="card">
|
<section class="card">
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue