left4me/l4d2web/services/timeago.py
mwiegand 3c4bd6880a
refactor(l4d2-web): detail-page UI — single panel, soft border, footer Delete
- Detail panels: softer (color-mix --line-soft) border. h2 sub-section
  spacing inside a single outer panel. admin and job_detail collapse to
  one panel each.
- Color tokens: --color-button-primary / --color-button-danger stay
  saturated in dark mode so white text on filled buttons stays readable.
- Site header: transparent, no full-width bar; aligned with panel-content
  width. No more sticky.
- Page-level Delete: low-contrast outline button at the page footer
  (left side, justify-content flex-start). Save buttons no longer
  full-width (.stack > button { justify-self: end }).
- form-actions-inline helper for right-aligned button rows.
- New service: l4d2web.services.timeago.humanize_delta — used by the
  upcoming server / overlay live-status partials.
- Server route: POST /servers/<id> renames the server (mirrors the
  overlay update pattern, returns 409 on per-user duplicate).
- Overlay route: POST /overlays/<id>/script handles `action` form value
  — `save_build` (default) or `save_reset_build` (wipes overlay dir
  before queuing build). Redirect lands on /overlays/<id> instead of
  the job page so users see the live status.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 01:26:57 +02:00

29 lines
823 B
Python

from datetime import UTC, datetime
def humanize_delta(then: datetime, now: datetime | None = None) -> str:
if now is None:
now = datetime.now(UTC)
if then.tzinfo is None:
then = then.replace(tzinfo=UTC)
if now.tzinfo is None:
now = now.replace(tzinfo=UTC)
seconds = int((now - then).total_seconds())
if seconds < 0:
seconds = 0
if seconds < 45:
return "just now"
if seconds < 90:
return "1 minute ago"
minutes = seconds // 60
if minutes < 60:
return f"{minutes} minutes ago"
hours = minutes // 60
if hours < 24:
return "1 hour ago" if hours == 1 else f"{hours} hours ago"
days = hours // 24
if days < 7:
return "1 day ago" if days == 1 else f"{days} days ago"
return then.date().isoformat()