fix: keep payment user cards in payments

This commit is contained in:
3252a8
2026-05-24 09:32:47 +03:00
parent aa65972483
commit 29b6e6eb24
5 changed files with 64 additions and 7 deletions
+1
View File
@@ -23,6 +23,7 @@ def setup_subscription_webapp_routes(app: web.Application) -> None:
index_route, index_route,
) )
app.router.add_get("/admin/users/{user_id:-?[0-9]+}", 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/payments/{payment_id:\\d+}", index_route)
app.router.add_get("/admin/support/{ticket_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) app.router.add_get("/auth/telegram/start", telegram_oauth_start_route)
+2
View File
@@ -80,6 +80,7 @@
import { DEV_MOCK, applyPreviewMock } from "./lib/webapp/previewMock.js"; import { DEV_MOCK, applyPreviewMock } from "./lib/webapp/previewMock.js";
import { import {
adminPaymentIdFromPath, adminPaymentIdFromPath,
adminPaymentsUserIdFromPath,
adminSectionFromPath, adminSectionFromPath,
adminUserIdFromPath, adminUserIdFromPath,
normalizeSection, normalizeSection,
@@ -790,6 +791,7 @@
onToast: (text) => showToast(text), onToast: (text) => showToast(text),
initialSection: adminSectionFromPath(window.location.pathname), initialSection: adminSectionFromPath(window.location.pathname),
initialPaymentId: adminPaymentIdFromPath(window.location.pathname), initialPaymentId: adminPaymentIdFromPath(window.location.pathname),
initialPaymentUserId: adminPaymentsUserIdFromPath(window.location.pathname),
initialUserId: adminUserIdFromPath(window.location.pathname), initialUserId: adminUserIdFromPath(window.location.pathname),
onSectionChange: handleAdminSectionChange, onSectionChange: handleAdminSectionChange,
onSettingsSaved: handleAdminPersistedSaved, onSettingsSaved: handleAdminPersistedSaved,
+26 -5
View File
@@ -77,6 +77,7 @@
export let onToast = () => {}; export let onToast = () => {};
export let initialSection = "stats"; export let initialSection = "stats";
export let initialPaymentId = null; export let initialPaymentId = null;
export let initialPaymentUserId = null;
export let initialUserId = null; export let initialUserId = null;
export let onSectionChange = () => {}; export let onSectionChange = () => {};
export let onSettingsSaved = () => {}; export let onSettingsSaved = () => {};
@@ -283,13 +284,24 @@
return match ? Number(match[1]) : null; 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() { function onPopState() {
active = readSectionFromPath(); active = readSectionFromPath();
sidebarOpen = false; sidebarOpen = false;
const userId = readUserIdFromPath(); const userId = readUserIdFromPath();
if (userId) { const paymentUserId = active === "payments" ? readPaymentUserIdFromPath() : null;
if (!$usersStore.openedUser || $usersStore.openedUser.user_id !== userId) { const contextualUserId = paymentUserId || userId;
usersStore.openUser(userId, { skipPush: true }); if (contextualUserId) {
if (!$usersStore.openedUser || $usersStore.openedUser.user_id !== contextualUserId) {
usersStore.openUser(contextualUserId, {
skipPush: true,
pathContext: paymentUserId ? "payments" : "users",
});
} }
} else if ($usersStore.openedUser) { } else if ($usersStore.openedUser) {
usersStore.closeUser({ skipPush: true }); usersStore.closeUser({ skipPush: true });
@@ -321,14 +333,16 @@
const uid = Number(userId); const uid = Number(userId);
// Synthetic email-only users use negative user_id; still a valid admin target. // Synthetic email-only users use negative user_id; still a valid admin target.
if (!Number.isFinite(uid) || uid === 0) return; if (!Number.isFinite(uid) || uid === 0) return;
const next = normalizeSection("users"); const next = normalizeSection("payments");
sidebarOpen = false; sidebarOpen = false;
if (active !== next) { if (active !== next) {
active = next; active = next;
usersStore.closeUser(); usersStore.closeUser();
paymentsStore.closePayment({ skipPush: true });
onSectionChange(next); onSectionChange(next);
} }
usersStore.openUser(uid); paymentsStore.closePayment({ skipPush: true });
usersStore.openUser(uid, { pathContext: "payments" });
} }
function resolvedAvatarUrl(user) { function resolvedAvatarUrl(user) {
@@ -460,6 +474,13 @@
paymentsStore.openPayment(initialPaymentId, { skipPush: true }); paymentsStore.openPayment(initialPaymentId, { skipPush: true });
} }
$: if (
active === "payments" &&
initialPaymentUserId &&
(!$usersStore.openedUser || $usersStore.openedUser.user_id !== initialPaymentUserId)
) {
usersStore.openUser(initialPaymentUserId, { skipPush: true, pathContext: "payments" });
}
</script> </script>
<div class="admin-screen-wrap" class:is-sidebar-open={sidebarOpen}> <div class="admin-screen-wrap" class:is-sidebar-open={sidebarOpen}>
+23 -2
View File
@@ -42,16 +42,34 @@ export function createUsersStore({ api, onToast, at }) {
}); });
let _activeRef = "stats"; // fallback if active isn't tracked let _activeRef = "stats"; // fallback if active isn't tracked
let _pathContext = null;
function setActive(active) { function setActive(active) {
_activeRef = active; _activeRef = active;
} }
function _setPathContext(context) {
if (context === "payments") {
_pathContext = "payments";
return;
}
if (_activeRef === "users") {
_pathContext = "users";
return;
}
_pathContext = null;
}
function _pushUserPath(userId) { function _pushUserPath(userId) {
if (typeof window === "undefined") return; if (typeof window === "undefined") return;
if (window.location.protocol === "file:") return; if (window.location.protocol === "file:") return;
if (_activeRef !== "users") return; let target = "";
const target = userId ? `/admin/users/${userId}` : `/admin/users`; if (_activeRef === "users") {
target = userId ? `/admin/users/${userId}` : `/admin/users`;
} else if (_activeRef === "payments" && _pathContext === "payments") {
target = userId ? `/admin/payments/users/${userId}` : `/admin/payments`;
}
if (!target) return;
if (window.location.pathname === target) return; if (window.location.pathname === target) return;
window.history.pushState(null, "", `${target}${window.location.search}${window.location.hash}`); window.history.pushState(null, "", `${target}${window.location.search}${window.location.hash}`);
} }
@@ -94,6 +112,7 @@ export function createUsersStore({ api, onToast, at }) {
const userId = const userId =
typeof userOrId === "object" && userOrId !== null ? userOrId.user_id : Number(userOrId); typeof userOrId === "object" && userOrId !== null ? userOrId.user_id : Number(userOrId);
if (!userId) return; if (!userId) return;
_setPathContext(opts.pathContext);
state.update((s) => ({ state.update((s) => ({
...s, ...s,
@@ -136,6 +155,7 @@ export function createUsersStore({ api, onToast, at }) {
onToast(res?.error || "load_failed"); onToast(res?.error || "load_failed");
state.update((s) => ({ ...s, openedUser: null })); state.update((s) => ({ ...s, openedUser: null }));
if (!opts.skipPush) _pushUserPath(null); if (!opts.skipPush) _pushUserPath(null);
_pathContext = null;
} }
} finally { } finally {
state.update((s) => ({ ...s, userDetailLoading: false })); state.update((s) => ({ ...s, userDetailLoading: false }));
@@ -162,6 +182,7 @@ export function createUsersStore({ api, onToast, at }) {
}; };
}); });
if (wasOpen && !opts.skipPush) _pushUserPath(null); if (wasOpen && !opts.skipPush) _pushUserPath(null);
_pathContext = null;
} }
async function loadUserLogs(page) { async function loadUserLogs(page) {
+12
View File
@@ -62,6 +62,14 @@ export function adminPaymentIdFromPath(pathname) {
return m ? Number(m[1]) : null; return m ? Number(m[1]) : null;
} }
export function adminPaymentsUserIdFromPath(pathname) {
const normalized = String(pathname || "")
.toLowerCase()
.replace(/\/+$/, "");
const m = normalized.match(/^\/admin\/payments\/users\/(-?\d+)$/);
return m ? Number(m[1]) : null;
}
export function supportTicketIdFromPath(pathname) { export function supportTicketIdFromPath(pathname) {
const normalized = String(pathname || "") const normalized = String(pathname || "")
.toLowerCase() .toLowerCase()
@@ -89,8 +97,12 @@ export function syncSectionPath(section, replace = false, adminSection = null, a
const supportTicketId = const supportTicketId =
adm === "support" ? adminSupportTicketIdFromPath(window.location.pathname) : null; adm === "support" ? adminSupportTicketIdFromPath(window.location.pathname) : null;
const paymentId = adm === "payments" ? adminPaymentIdFromPath(window.location.pathname) : null; const paymentId = adm === "payments" ? adminPaymentIdFromPath(window.location.pathname) : null;
const paymentUserId =
adm === "payments" ? adminPaymentsUserIdFromPath(window.location.pathname) : null;
if (adm === "users" && uid) targetPath = `/admin/users/${uid}`; if (adm === "users" && uid) targetPath = `/admin/users/${uid}`;
else if (adm === "support" && supportTicketId) targetPath = `/admin/support/${supportTicketId}`; else if (adm === "support" && supportTicketId) targetPath = `/admin/support/${supportTicketId}`;
else if (adm === "payments" && paymentUserId)
targetPath = `/admin/payments/users/${paymentUserId}`;
else if (adm === "payments" && paymentId) targetPath = `/admin/payments/${paymentId}`; else if (adm === "payments" && paymentId) targetPath = `/admin/payments/${paymentId}`;
else targetPath = `/admin/${adm}`; else targetPath = `/admin/${adm}`;
} }