fix: keep payment user cards in payments
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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}>
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -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}`;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user