diff --git a/backend/bot/app/web/webapp/routes.py b/backend/bot/app/web/webapp/routes.py index c5b9716..f6e171d 100644 --- a/backend/bot/app/web/webapp/routes.py +++ b/backend/bot/app/web/webapp/routes.py @@ -23,6 +23,7 @@ def setup_subscription_webapp_routes(app: web.Application) -> None: index_route, ) app.router.add_get("/admin/users/{user_id:-?[0-9]+}", index_route) + app.router.add_get("/admin/payments/users/{user_id:-?[0-9]+}", index_route) app.router.add_get("/admin/payments/{payment_id:\\d+}", index_route) app.router.add_get("/admin/support/{ticket_id:\\d+}", index_route) app.router.add_get("/auth/telegram/start", telegram_oauth_start_route) diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index 63b5a3b..fc60c71 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -80,6 +80,7 @@ import { DEV_MOCK, applyPreviewMock } from "./lib/webapp/previewMock.js"; import { adminPaymentIdFromPath, + adminPaymentsUserIdFromPath, adminSectionFromPath, adminUserIdFromPath, normalizeSection, @@ -790,6 +791,7 @@ onToast: (text) => showToast(text), initialSection: adminSectionFromPath(window.location.pathname), initialPaymentId: adminPaymentIdFromPath(window.location.pathname), + initialPaymentUserId: adminPaymentsUserIdFromPath(window.location.pathname), initialUserId: adminUserIdFromPath(window.location.pathname), onSectionChange: handleAdminSectionChange, onSettingsSaved: handleAdminPersistedSaved, diff --git a/frontend/src/admin/AdminPanel.svelte b/frontend/src/admin/AdminPanel.svelte index a0c8e47..6b54b0b 100644 --- a/frontend/src/admin/AdminPanel.svelte +++ b/frontend/src/admin/AdminPanel.svelte @@ -77,6 +77,7 @@ export let onToast = () => {}; export let initialSection = "stats"; export let initialPaymentId = null; + export let initialPaymentUserId = null; export let initialUserId = null; export let onSectionChange = () => {}; export let onSettingsSaved = () => {}; @@ -283,13 +284,24 @@ return match ? Number(match[1]) : null; } + function readPaymentUserIdFromPath() { + if (typeof window === "undefined") return null; + const match = window.location.pathname.match(/^\/admin\/payments\/users\/(-?\d+)$/); + return match ? Number(match[1]) : null; + } + function onPopState() { active = readSectionFromPath(); sidebarOpen = false; const userId = readUserIdFromPath(); - if (userId) { - if (!$usersStore.openedUser || $usersStore.openedUser.user_id !== userId) { - usersStore.openUser(userId, { skipPush: true }); + const paymentUserId = active === "payments" ? readPaymentUserIdFromPath() : null; + const contextualUserId = paymentUserId || userId; + if (contextualUserId) { + if (!$usersStore.openedUser || $usersStore.openedUser.user_id !== contextualUserId) { + usersStore.openUser(contextualUserId, { + skipPush: true, + pathContext: paymentUserId ? "payments" : "users", + }); } } else if ($usersStore.openedUser) { usersStore.closeUser({ skipPush: true }); @@ -321,14 +333,16 @@ const uid = Number(userId); // Synthetic email-only users use negative user_id; still a valid admin target. if (!Number.isFinite(uid) || uid === 0) return; - const next = normalizeSection("users"); + const next = normalizeSection("payments"); sidebarOpen = false; if (active !== next) { active = next; usersStore.closeUser(); + paymentsStore.closePayment({ skipPush: true }); onSectionChange(next); } - usersStore.openUser(uid); + paymentsStore.closePayment({ skipPush: true }); + usersStore.openUser(uid, { pathContext: "payments" }); } function resolvedAvatarUrl(user) { @@ -460,6 +474,13 @@ paymentsStore.openPayment(initialPaymentId, { skipPush: true }); } + $: if ( + active === "payments" && + initialPaymentUserId && + (!$usersStore.openedUser || $usersStore.openedUser.user_id !== initialPaymentUserId) + ) { + usersStore.openUser(initialPaymentUserId, { skipPush: true, pathContext: "payments" }); + }