diff --git a/admin.html b/admin.html index 933245f..e2c6cb4 100644 --- a/admin.html +++ b/admin.html @@ -703,6 +703,10 @@ function statCard(label, value, color, icon, delay) { `; } +function rowAttr(i) { + return `class="reveal" style="animation-delay:${Math.min(i, 10) * 0.025}s"`; +} + async function loadTraffic() { const t = await api("/admin/api/traffic"); const grid = document.getElementById("traffic-stat-grid"); @@ -713,8 +717,8 @@ async function loadTraffic() { ].map(([l, v, c, ic], i) => statCard(l, v, c, ic, i * 0.05)).join(""); const body = document.getElementById("traffic-body"); - body.innerHTML = t.per_subscription.length ? t.per_subscription.map((r) => ` - ${esc(r.username)}${esc(r.node_label)}${r.up_fmt}${r.down_fmt}${r.total_fmt} + body.innerHTML = t.per_subscription.length ? t.per_subscription.map((r, i) => ` + ${esc(r.username)}${esc(r.node_label)}${r.up_fmt}${r.down_fmt}${r.total_fmt} `).join("") : '
Пока нет данных по трафику
'; } @@ -756,8 +760,8 @@ async function loadDashboard() { const subs = await api("/admin/api/subscriptions"); const body = document.getElementById("recent-subs-body"); const recent = subs.slice(0, 8); - body.innerHTML = recent.length ? recent.map((s) => ` - ${s.username ? "@" + esc(s.username) : "tg" + s.tg_id}${esc(s.node_label)}${esc(s.plan_label)} + body.innerHTML = recent.length ? recent.map((s, i) => ` + ${s.username ? "@" + esc(s.username) : "tg" + s.tg_id}${esc(s.node_label)}${esc(s.plan_label)} ${fmtDate(s.expires_at)}${statusBadge(s.active, s.days_left)} `).join("") : '
Пока нет подписок
'; } @@ -765,8 +769,8 @@ async function loadDashboard() { async function loadSubscriptions() { const subs = await api("/admin/api/subscriptions"); const body = document.getElementById("subs-body"); - body.innerHTML = subs.length ? subs.map((s) => ` - ${s.username ? "@" + esc(s.username) : "tg" + s.tg_id}${esc(s.node_label)}${esc(s.plan_label)} + body.innerHTML = subs.length ? subs.map((s, i) => ` + ${s.username ? "@" + esc(s.username) : "tg" + s.tg_id}${esc(s.node_label)}${esc(s.plan_label)} ${fmtDate(s.created_at)}${fmtDate(s.expires_at)}${statusBadge(s.active, s.days_left)} @@ -803,8 +807,8 @@ function switchUserTab(tab) { } function renderUcSubs(subs) { const list = document.getElementById("uc-subs-list"); - list.innerHTML = subs.length ? subs.map((s) => ` -
+ list.innerHTML = subs.length ? subs.map((s, i) => ` +
${esc(s.node_label)} — ${esc(s.plan_label)}
до ${fmtDate(s.expires_at)} · ${statusBadge(s.active, s.days_left)}
@@ -822,8 +826,8 @@ async function grantSubscription() { } function renderDevices(devices) { const list = document.getElementById("devices-list"); - list.innerHTML = devices.length ? devices.map((d) => ` -
+ list.innerHTML = devices.length ? devices.map((d, i) => ` +
${esc(d.device_model || d.device_os || "Неизвестное устройство")}
${esc(d.device_os || "")} · с ${fmtDate(d.first_seen)}
@@ -862,8 +866,8 @@ async function loadGifts() { const codes = await api("/admin/api/gift-codes"); const body = document.getElementById("gifts-body"); - body.innerHTML = codes.length ? codes.map((c) => ` - ${esc(c.node_label)}${esc(c.plan_label)}${fmtDate(c.created_at)} + body.innerHTML = codes.length ? codes.map((c, i) => ` + ${esc(c.node_label)}${esc(c.plan_label)}${fmtDate(c.created_at)} ${c.used_by ? 'использован' : 'свободен'} `).join("") : '
Пока нет гифт-кодов
'; @@ -883,8 +887,8 @@ async function loadNodes() { const nodes = await api("/admin/api/nodes"); nodesCache = nodes; const body = document.getElementById("nodes-body"); - body.innerHTML = nodes.map((n) => ` - + body.innerHTML = nodes.map((n, i) => ` + ${esc(n.label)}${esc(n.address) || "—"}${n.port && n.address ? ":" + n.port : ""} ${n.kind === "local" ? "локальная" : n.kind === "managed" ? "управляемая" : "внешняя"} ${n.status === "pending" ? 'ожидает установки' : (n.enabled ? 'включена' : 'выключена')}