From 7842ddf1b859c8c47de532a35d0f8a1219d8d358 Mon Sep 17 00:00:00 2001 From: orohi Date: Sat, 25 Jul 2026 01:15:54 +0300 Subject: [PATCH] Add admin link IDs, tabs for extend/traffic, and pagination --- app/routers/admin.py | 88 ++++++++++++++++++++++++---- app/static/css/style.css | 72 +++++++++++++++++++++++ app/templates/admin/dashboard.html | 50 ++++++++++++++-- app/templates/admin/link_detail.html | 2 +- 4 files changed, 194 insertions(+), 18 deletions(-) diff --git a/app/routers/admin.py b/app/routers/admin.py index 1e57324..869fbbc 100644 --- a/app/routers/admin.py +++ b/app/routers/admin.py @@ -64,20 +64,62 @@ async def admin_dashboard(request: Request): return RedirectResponse("/admin/login", status_code=303) settings = get_settings() + tab = (request.query_params.get("type") or "extend").strip().lower() + if tab not in ("extend", "traffic", "all"): + tab = "extend" + + try: + page = max(1, int(request.query_params.get("page") or 1)) + except ValueError: + page = 1 + per_page = 10 + offset = (page - 1) * per_page + db = await get_db() try: - cursor = await db.execute( + if tab == "all": + count_sql = "SELECT COUNT(*) AS c FROM guest_links" + list_sql = """ + SELECT l.*, + (SELECT COUNT(*) FROM guest_claims c WHERE c.link_id = l.id) AS claims + FROM guest_links l + ORDER BY l.id DESC + LIMIT ? OFFSET ? """ - SELECT l.*, - (SELECT COUNT(*) FROM guest_claims c WHERE c.link_id = l.id) AS claims - FROM guest_links l - ORDER BY l.id DESC + count_args: tuple = () + list_args: tuple = (per_page, offset) + else: + count_sql = "SELECT COUNT(*) AS c FROM guest_links WHERE link_type = ?" + list_sql = """ + SELECT l.*, + (SELECT COUNT(*) FROM guest_claims c WHERE c.link_id = l.id) AS claims + FROM guest_links l + WHERE l.link_type = ? + ORDER BY l.id DESC + LIMIT ? OFFSET ? """ - ) + count_args = (tab,) + list_args = (tab, per_page, offset) + + cursor = await db.execute(count_sql, count_args) + total = int((await cursor.fetchone())["c"]) + cursor = await db.execute(list_sql, list_args) links = [dict(row) for row in await cursor.fetchall()] + + cursor = await db.execute( + "SELECT link_type, COUNT(*) AS c FROM guest_links GROUP BY link_type" + ) + counts = {"extend": 0, "traffic": 0, "all": 0} + for row in await cursor.fetchall(): + counts[row["link_type"]] = int(row["c"]) + counts["all"] += int(row["c"]) finally: await db.close() + total_pages = max(1, (total + per_page - 1) // per_page) + if page > total_pages: + page = total_pages + site_base = public_base_url(request) return templates.TemplateResponse( "admin/dashboard.html", @@ -87,6 +129,12 @@ async def admin_dashboard(request: Request): "links": links, "site_base": site_base, "flash": request.query_params.get("flash"), + "tab": tab, + "page": page, + "per_page": per_page, + "total": total, + "total_pages": total_pages, + "counts": counts, }, ) @@ -159,11 +207,19 @@ async def create_link( finally: await db.close() - return RedirectResponse("/admin?flash=created", status_code=303) + return RedirectResponse( + f"/admin?type={link_type}&flash=created", status_code=303 + ) @router.post("/links/{link_id}/toggle") -async def toggle_link(link_id: int, _admin: str = Depends(get_current_admin)): +async def toggle_link( + request: Request, + link_id: int, + _admin: str = Depends(get_current_admin), +): + tab = (request.query_params.get("type") or "extend").strip() + page = request.query_params.get("page") or "1" db = await get_db() try: await db.execute( @@ -173,18 +229,28 @@ async def toggle_link(link_id: int, _admin: str = Depends(get_current_admin)): await db.commit() finally: await db.close() - return RedirectResponse("/admin?flash=toggled", status_code=303) + return RedirectResponse( + f"/admin?type={tab}&page={page}&flash=toggled", status_code=303 + ) @router.post("/links/{link_id}/delete") -async def delete_link(link_id: int, _admin: str = Depends(get_current_admin)): +async def delete_link( + request: Request, + link_id: int, + _admin: str = Depends(get_current_admin), +): + tab = (request.query_params.get("type") or "extend").strip() + page = request.query_params.get("page") or "1" db = await get_db() try: await db.execute("DELETE FROM guest_links WHERE id = ?", (link_id,)) await db.commit() finally: await db.close() - return RedirectResponse("/admin?flash=deleted", status_code=303) + return RedirectResponse( + f"/admin?type={tab}&page={page}&flash=deleted", status_code=303 + ) @router.get("/links/{link_id}", response_class=HTMLResponse) diff --git a/app/static/css/style.css b/app/static/css/style.css index f4b755c..54e4e68 100644 --- a/app/static/css/style.css +++ b/app/static/css/style.css @@ -530,6 +530,78 @@ th { max-width: min(520px, 100%); } +.admin-tabs { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + margin: 0 0 1rem; + border-bottom: 1px solid var(--line); + padding-bottom: 0.75rem; +} + +.admin-tab { + display: inline-flex; + align-items: center; + gap: 0.45rem; + padding: 0.55rem 0.9rem; + border: 1px solid var(--line); + color: var(--mist); + font-size: 0.9rem; +} + +.admin-tab:hover { + color: var(--fog); + border-color: rgba(212, 255, 63, 0.35); +} + +.admin-tab.is-active { + color: var(--ink); + background: var(--signal); + border-color: var(--signal); +} + +.tab-count { + font-size: 0.75rem; + opacity: 0.85; +} + +.id-pill { + font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; + font-weight: 600; + color: var(--signal); +} + +.list-meta { + margin: 0 0 0.85rem; + font-size: 0.85rem; +} + +.pager { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.4rem; + margin-top: 1.25rem; +} + +.pager .btn { + padding: 0.4rem 0.7rem; + font-size: 0.8rem; +} + +.pager-current { + display: inline-flex; + align-items: center; + justify-content: center; + min-width: 2rem; + padding: 0.4rem 0.7rem; + background: rgba(212, 255, 63, 0.15); + color: var(--signal); + font-size: 0.85rem; + font-weight: 600; +} + + .login-shell { min-height: 100vh; display: grid; diff --git a/app/templates/admin/dashboard.html b/app/templates/admin/dashboard.html index 9b85c07..d75626b 100644 --- a/app/templates/admin/dashboard.html +++ b/app/templates/admin/dashboard.html @@ -37,8 +37,8 @@