fix(files): a steady list header, every user on the Storage tab, the name gets the row's room

- The Files list starts at the scroller's top: the padding above it let the sticky header ride up
  12px on the first scroll, then stop.
- Users -> Storage lists every user with their quota, not only those already holding a file — a
  quota is set before anyone downloads.
- On a wide Files list only the "…" holds a place; Copy link, Share and Save come up over the date
  while the row is hovered or holds the keyboard (a card keeps them in its line). Keeps is narrower.
This commit is contained in:
2026-10-11 20:33:48 +02:00
parent 078194731b
commit 6707550513
5 changed files with 221 additions and 171 deletions
+8 -9
View File
@@ -856,19 +856,18 @@ def admin_storage(admin: User = Depends(admin_user), db: Session = Depends(get_d
select(func.count(), func.coalesce(func.sum(MediaAsset.size_bytes), 0)).where(quota.ON_DISK)
).one()
total_assets = db.execute(select(func.count()).select_from(MediaAsset)).scalar()
per_user = db.execute(
select(DownloadJob.user_id, func.count(func.distinct(DownloadJob.asset_id)))
.where(DownloadJob.asset_id.is_not(None))
.group_by(DownloadJob.user_id)
).all()
uids = [uid for uid, _ in per_user]
emails = {u.id: u.email for u in db.execute(select(User).where(User.id.in_(uids))).scalars()}
# EVERY user, not only those holding a file: a quota is set before anyone downloads.
emails: dict[int, str] = {
uid: email
for uid, email in db.execute(select(User.id, User.email).order_by(User.email)).tuples()
}
uids = list(emails)
# What each one's Files lists: their finished files.
files: dict[int, int] = {
uid: n
for uid, n in db.execute(
select(DownloadJob.user_id, func.count())
.where(DownloadJob.user_id.in_(uids), DownloadJob.status == JobStatus.DONE)
.where(DownloadJob.status == JobStatus.DONE)
.group_by(DownloadJob.user_id)
).tuples()
}
@@ -877,7 +876,7 @@ def admin_storage(admin: User = Depends(admin_user), db: Session = Depends(get_d
lim = quota.resolve(db, uid)
return {
"user_id": uid,
"email": emails.get(uid),
"email": emails[uid],
"footprint_bytes": quota.footprint(db, uid),
"files": files.get(uid, 0),
"max_bytes": lim.max_bytes,