diff --git a/auth.py b/auth.py index 340f783..317dcab 100644 --- a/auth.py +++ b/auth.py @@ -230,7 +230,9 @@ def admin_stats(db: str = DEFAULT_DB) -> dict: con = _con(db) try: users = con.execute("SELECT COUNT(*) FROM users").fetchone()[0] - unused = con.execute("SELECT COUNT(*) FROM invites WHERE used_by IS NULL").fetchone()[0] + open_codes = [c for (c,) in con.execute( + "SELECT code FROM invites WHERE used_by IS NULL ORDER BY created DESC")] + unused = len(open_codes) presets = con.execute("SELECT COUNT(*) FROM presets").fetchone()[0] accounts = [{"username": u, "email": e, "joined": time.strftime("%Y-%m-%d", time.localtime(c))} @@ -240,8 +242,8 @@ def admin_stats(db: str = DEFAULT_DB) -> dict: for u, t, c in con.execute("SELECT username,text,created FROM feedback ORDER BY created DESC LIMIT 100")] finally: con.close() - return {"users": users, "unused_codes": unused, "presets": presets, - "accounts": accounts, "feedback": fb} + return {"users": users, "unused_codes": unused, "open_codes": open_codes, + "presets": presets, "accounts": accounts, "feedback": fb} # ---- presets (a full personal patch, stored as one JSON blob) --------------- diff --git a/viz/index.html b/viz/index.html index 62b1006..424d5f0 100644 --- a/viz/index.html +++ b/viz/index.html @@ -285,6 +285,12 @@ #account .ghost { background: rgba(255,255,255,0.05); color: #b9c8e2; border: 1px solid rgba(120,150,200,0.2); border-radius: 7px; padding: 6px 10px; cursor: pointer; font-size: 11px; } #account .code { font-family: monospace; color: #7cffb2; margin-left: 8px; font-size: 12px; } + #account .codes { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 2px; } + #account .codechip { font-family: monospace; color: #7cffb2; font-size: 12px; cursor: pointer; + background: rgba(124,255,178,0.08); border: 1px solid rgba(124,255,178,0.3); border-radius: 6px; + padding: 4px 9px; user-select: text; -webkit-user-select: text; transition: background 0.15s; } + #account .codechip:hover { background: rgba(124,255,178,0.18); } + #account .codechip.copied { color: #fff; border-color: #7cffb2; background: rgba(124,255,178,0.3); } /* ---- gated landing page (shown until you sign in) ---- */ #landing { @@ -5791,19 +5797,56 @@ const s = r.data, box = acctBox.querySelector("#adminbox"); const plural = (n, w) => n + " " + w + (n === 1 ? "" : "s"); box.style.display = "block"; + const codes = s.open_codes || []; box.innerHTML = "