diff --git a/app/web/routes.py b/app/web/routes.py index 1f7bd30..54aad43 100644 --- a/app/web/routes.py +++ b/app/web/routes.py @@ -15,6 +15,14 @@ router = APIRouter() templates = Jinja2Templates(directory=str(Path(__file__).resolve().parent / "templates")) +def _next_sync_tick(request: Request): + scheduler = getattr(request.app.state, "scheduler", None) + return getattr(scheduler, "next_tick", None) + + +templates.env.globals["next_sync_tick"] = _next_sync_tick + + def _get_user_or_404(repository: UserRepository, user_id: int) -> SyncUser: user = repository.get(user_id) if user is None: diff --git a/app/web/static/app.js b/app/web/static/app.js new file mode 100644 index 0000000..62b24cc --- /dev/null +++ b/app/web/static/app.js @@ -0,0 +1,10 @@ +document.addEventListener("DOMContentLoaded", () => { + document.querySelectorAll("time[data-utc]").forEach((el) => { + const date = new Date(el.dataset.utc); + if (Number.isNaN(date.getTime())) { + return; + } + el.textContent = date.toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short" }); + el.title = `${el.dataset.utc} (UTC)`; + }); +}); diff --git a/app/web/static/style.css b/app/web/static/style.css index 3119407..2d97e0d 100644 --- a/app/web/static/style.css +++ b/app/web/static/style.css @@ -64,12 +64,36 @@ a:hover { border-radius: 7px; } +.topbar-right { + display: flex; + align-items: center; + gap: 1.25rem; + flex-wrap: wrap; +} + .topbar nav { display: flex; gap: 1.25rem; font-size: 0.9rem; } +.sync-status { + display: inline-flex; + align-items: center; + gap: 0.35rem; + padding: 0.3rem 0.7rem; + border-radius: 999px; + background: var(--info-bg); + color: var(--info); + font-size: 0.78rem; + font-weight: 600; + white-space: nowrap; +} + +.sync-status time { + font-weight: 700; +} + .container { max-width: 960px; margin: 0 auto; diff --git a/app/web/templates/base.html b/app/web/templates/base.html index cbec323..3eb2b87 100644 --- a/app/web/templates/base.html +++ b/app/web/templates/base.html @@ -9,19 +9,26 @@ +