Merge GitHub dev into GitLab dev
This commit is contained in:
@@ -223,6 +223,8 @@
|
||||
}
|
||||
let sidebarOpen = false;
|
||||
let isCompact = false;
|
||||
let dismissedUserRouteKey = "";
|
||||
let lastUserRouteKey = "";
|
||||
let adminLanguageMenuOpen = false;
|
||||
let adminLanguageClickGuard = false;
|
||||
let adminLanguageClickGuardArmed = false;
|
||||
@@ -379,6 +381,7 @@
|
||||
const uid = Number(userId);
|
||||
// Synthetic email-only users use negative user_id; still a valid admin target.
|
||||
if (!Number.isFinite(uid) || uid === 0) return;
|
||||
dismissedUserRouteKey = "";
|
||||
const next = normalizeSection("payments");
|
||||
sidebarOpen = false;
|
||||
if (active !== next) {
|
||||
@@ -395,6 +398,7 @@
|
||||
function openLogsUserCard(userId) {
|
||||
const uid = Number(userId);
|
||||
if (!Number.isFinite(uid) || uid === 0) return;
|
||||
dismissedUserRouteKey = "";
|
||||
const next = normalizeSection("logs");
|
||||
sidebarOpen = false;
|
||||
if (active !== next) {
|
||||
@@ -410,16 +414,24 @@
|
||||
function openUserCard(userId) {
|
||||
const uid = Number(userId);
|
||||
if (!Number.isFinite(uid) || uid === 0) return;
|
||||
const next = normalizeSection("users");
|
||||
dismissedUserRouteKey = "";
|
||||
sidebarOpen = false;
|
||||
if (active !== next) {
|
||||
active = next;
|
||||
paymentsStore.closePayment({ skipPush: true });
|
||||
supportStore.closeTicketView({ skipPush: true });
|
||||
onSectionChange(next, uid);
|
||||
usersStore.setActive(active);
|
||||
usersStore.openUser(uid, { skipPush: true, pathContext: active });
|
||||
}
|
||||
|
||||
function userRouteKey(section = active) {
|
||||
if (section === "users" && initialUserId) return `users:${initialUserId}`;
|
||||
if (section === "payments" && initialPaymentUserId) return `payments:${initialPaymentUserId}`;
|
||||
return "";
|
||||
}
|
||||
|
||||
function closeUserCard() {
|
||||
dismissedUserRouteKey = userRouteKey();
|
||||
usersStore.closeUser({ skipPush: true });
|
||||
if (active === "users" || active === "payments") {
|
||||
onSectionChange(active, 0);
|
||||
}
|
||||
usersStore.setActive(next);
|
||||
usersStore.openUser(uid);
|
||||
}
|
||||
|
||||
function resolvedAvatarUrl(user) {
|
||||
@@ -534,9 +546,18 @@
|
||||
$: sectionFade = reduceMotion ? { duration: 0 } : { duration: 200 };
|
||||
$: sidebarBackdropFade = reduceMotion ? { duration: 0 } : { duration: 180 };
|
||||
|
||||
$: {
|
||||
const currentUserRouteKey = userRouteKey();
|
||||
if (currentUserRouteKey !== lastUserRouteKey) {
|
||||
if (currentUserRouteKey !== dismissedUserRouteKey) dismissedUserRouteKey = "";
|
||||
lastUserRouteKey = currentUserRouteKey;
|
||||
}
|
||||
}
|
||||
|
||||
$: if (
|
||||
active === "users" &&
|
||||
initialUserId &&
|
||||
dismissedUserRouteKey !== `users:${initialUserId}` &&
|
||||
(!$usersStore.openedUser || $usersStore.openedUser.user_id !== initialUserId)
|
||||
) {
|
||||
usersStore.openUser(initialUserId, { skipPush: true });
|
||||
@@ -553,6 +574,7 @@
|
||||
$: if (
|
||||
active === "payments" &&
|
||||
initialPaymentUserId &&
|
||||
dismissedUserRouteKey !== `payments:${initialPaymentUserId}` &&
|
||||
(!$usersStore.openedUser || $usersStore.openedUser.user_id !== initialPaymentUserId)
|
||||
) {
|
||||
usersStore.openUser(initialPaymentUserId, { skipPush: true, pathContext: "payments" });
|
||||
@@ -893,4 +915,5 @@
|
||||
{trafficPercentValue}
|
||||
{trafficLeftLabel}
|
||||
{trafficOfLabel}
|
||||
onClose={closeUserCard}
|
||||
/>
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
export let userTelegramProfileLink = () => "";
|
||||
export let userTelegramProfileLinkKind = () => "";
|
||||
export let openTelegramProfileLink = () => false;
|
||||
export let onClose = () => usersStore.closeUser();
|
||||
|
||||
let avatarPreviewOpen = false;
|
||||
let avatarPreviewUrl = "";
|
||||
@@ -198,7 +199,7 @@
|
||||
: ""}
|
||||
description={openedUser?.username ? "@" + openedUser.username : ""}
|
||||
closeLabel={at("close", {}, "Закрыть")}
|
||||
onclose={usersStore.closeUser}
|
||||
onclose={onClose}
|
||||
class="admin-dialog admin-user-dialog"
|
||||
>
|
||||
{#if openedUser}
|
||||
|
||||
@@ -54,6 +54,58 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
|
||||
let _activeRef = "stats"; // fallback if active isn't tracked
|
||||
let _pathContext = null;
|
||||
let _openUserRequestId = 0;
|
||||
|
||||
function _closedUserModalState() {
|
||||
return {
|
||||
openedUser: null,
|
||||
openedUserDetail: null,
|
||||
userDetailLoading: false,
|
||||
userMessageDraft: "",
|
||||
userExtendDays: 30,
|
||||
userExtendHwidDevices: true,
|
||||
userDeleteOpen: false,
|
||||
userBanConfirmOpen: false,
|
||||
userMessageConfirmOpen: false,
|
||||
userReferralsOpen: false,
|
||||
userReferralsLoading: false,
|
||||
userReferrals: [],
|
||||
userReferralsTotal: 0,
|
||||
userReferralsPage: 0,
|
||||
userReferralsInviter: null,
|
||||
userDetailTab: "profile",
|
||||
premiumUnlimitedDraft: false,
|
||||
premiumBonusGbDraft: "",
|
||||
regularUnlimitedDraft: false,
|
||||
regularBonusGbDraft: "",
|
||||
hwidUnlimitedDraft: false,
|
||||
hwidDeviceLimitDraft: "",
|
||||
grantTrafficGbDraft: "",
|
||||
grantTrafficKindDraft: "regular",
|
||||
userLogs: [],
|
||||
userLogsTotal: 0,
|
||||
userLogsPage: 0,
|
||||
userLogsLoading: false,
|
||||
userLogsLoaded: false,
|
||||
userLogsUserId: null,
|
||||
};
|
||||
}
|
||||
|
||||
function _openingUserModalState(user, userId) {
|
||||
return {
|
||||
..._closedUserModalState(),
|
||||
openedUser: user,
|
||||
userDetailLoading: true,
|
||||
userDetailTab: "subscription",
|
||||
userLogsUserId: userId,
|
||||
};
|
||||
}
|
||||
|
||||
function _isCurrentUserRequest(s, requestId, userId) {
|
||||
return (
|
||||
requestId === _openUserRequestId && Boolean(s.openedUser) && s.openedUser.user_id === userId
|
||||
);
|
||||
}
|
||||
|
||||
function setActive(active) {
|
||||
_activeRef = active;
|
||||
@@ -124,31 +176,15 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
const userId =
|
||||
typeof userOrId === "object" && userOrId !== null ? userOrId.user_id : Number(userOrId);
|
||||
if (!userId) return;
|
||||
const requestId = ++_openUserRequestId;
|
||||
_setPathContext(opts.pathContext);
|
||||
const openedUser =
|
||||
typeof userOrId === "object" && userOrId !== null ? userOrId : { user_id: userId };
|
||||
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
openedUser:
|
||||
typeof userOrId === "object" && userOrId !== null ? userOrId : { user_id: userId },
|
||||
openedUserDetail: null,
|
||||
userMessageDraft: "",
|
||||
userMessageConfirmOpen: false,
|
||||
userExtendDays: 30,
|
||||
userExtendHwidDevices: true,
|
||||
userDetailLoading: true,
|
||||
userDetailTab: "subscription",
|
||||
userReferralsOpen: false,
|
||||
userReferralsLoading: false,
|
||||
userReferrals: [],
|
||||
userReferralsTotal: 0,
|
||||
userReferralsPage: 0,
|
||||
userReferralsInviter: null,
|
||||
userLogs: [],
|
||||
userLogsTotal: 0,
|
||||
userLogsPage: 0,
|
||||
userLogsLoading: false,
|
||||
userLogsLoaded: false,
|
||||
userLogsUserId: userId,
|
||||
..._openingUserModalState(openedUser, userId),
|
||||
userActionBusy: s.userActionBusy,
|
||||
}));
|
||||
|
||||
if (!opts.skipPush) _pushUserPath(userId);
|
||||
@@ -161,54 +197,52 @@ export function createUsersStore({ api, onToast, at, routePrefix = "" }) {
|
||||
const hasHwidLimit =
|
||||
sub?.hwid_device_limit !== null && sub?.hwid_device_limit !== undefined;
|
||||
const hwidLimit = hasHwidLimit ? Number(sub?.hwid_device_limit) : null;
|
||||
state.update((s) => ({
|
||||
...s,
|
||||
openedUserDetail: res,
|
||||
openedUser: res.user ? { ...res.user, ...s.openedUser, ...res.user } : s.openedUser,
|
||||
premiumUnlimitedDraft: Boolean(sub?.premium_unlimited_override),
|
||||
premiumBonusGbDraft: bonusBytes > 0 ? +(bonusBytes / 1024 ** 3).toFixed(2) : "",
|
||||
regularUnlimitedDraft: Boolean(sub?.regular_unlimited_override),
|
||||
regularBonusGbDraft:
|
||||
regularBonusBytes > 0 ? +(regularBonusBytes / 1024 ** 3).toFixed(2) : "",
|
||||
hwidUnlimitedDraft: hasHwidLimit && hwidLimit === 0,
|
||||
hwidDeviceLimitDraft: hasHwidLimit && hwidLimit > 0 ? String(hwidLimit) : "",
|
||||
grantTrafficGbDraft: "",
|
||||
grantTrafficKindDraft: "regular",
|
||||
}));
|
||||
state.update((s) => {
|
||||
if (!_isCurrentUserRequest(s, requestId, userId)) return s;
|
||||
return {
|
||||
...s,
|
||||
openedUserDetail: res,
|
||||
openedUser: res.user ? { ...res.user, ...s.openedUser, ...res.user } : s.openedUser,
|
||||
premiumUnlimitedDraft: Boolean(sub?.premium_unlimited_override),
|
||||
premiumBonusGbDraft: bonusBytes > 0 ? +(bonusBytes / 1024 ** 3).toFixed(2) : "",
|
||||
regularUnlimitedDraft: Boolean(sub?.regular_unlimited_override),
|
||||
regularBonusGbDraft:
|
||||
regularBonusBytes > 0 ? +(regularBonusBytes / 1024 ** 3).toFixed(2) : "",
|
||||
hwidUnlimitedDraft: hasHwidLimit && hwidLimit === 0,
|
||||
hwidDeviceLimitDraft: hasHwidLimit && hwidLimit > 0 ? String(hwidLimit) : "",
|
||||
grantTrafficGbDraft: "",
|
||||
grantTrafficKindDraft: "regular",
|
||||
};
|
||||
});
|
||||
} else {
|
||||
onToast(res?.error || "load_failed");
|
||||
state.update((s) => ({ ...s, openedUser: null }));
|
||||
if (!opts.skipPush) _pushUserPath(null);
|
||||
_pathContext = null;
|
||||
let shouldClearPath = false;
|
||||
let shouldShowError = false;
|
||||
state.update((s) => {
|
||||
if (!_isCurrentUserRequest(s, requestId, userId)) return s;
|
||||
shouldShowError = true;
|
||||
shouldClearPath = true;
|
||||
_pathContext = null;
|
||||
return { ...s, ..._closedUserModalState() };
|
||||
});
|
||||
if (shouldShowError) onToast(res?.error || "load_failed");
|
||||
if (shouldClearPath && !opts.skipPush) _pushUserPath(null);
|
||||
}
|
||||
} finally {
|
||||
state.update((s) => ({ ...s, userDetailLoading: false }));
|
||||
state.update((s) => {
|
||||
if (!_isCurrentUserRequest(s, requestId, userId)) return s;
|
||||
return { ...s, userDetailLoading: false };
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function closeUser(opts = {}) {
|
||||
let wasOpen = false;
|
||||
_openUserRequestId += 1;
|
||||
state.update((s) => {
|
||||
wasOpen = Boolean(s.openedUser);
|
||||
return {
|
||||
...s,
|
||||
openedUser: null,
|
||||
openedUserDetail: null,
|
||||
userDeleteOpen: false,
|
||||
userBanConfirmOpen: false,
|
||||
userMessageConfirmOpen: false,
|
||||
userReferralsOpen: false,
|
||||
userReferralsLoading: false,
|
||||
userReferrals: [],
|
||||
userReferralsTotal: 0,
|
||||
userReferralsPage: 0,
|
||||
userReferralsInviter: null,
|
||||
userLogs: [],
|
||||
userLogsTotal: 0,
|
||||
userLogsPage: 0,
|
||||
userLogsLoading: false,
|
||||
userLogsLoaded: false,
|
||||
userLogsUserId: null,
|
||||
..._closedUserModalState(),
|
||||
};
|
||||
});
|
||||
if (wasOpen && !opts.skipPush) _pushUserPath(null);
|
||||
|
||||
@@ -120,9 +120,11 @@ export function syncSectionPath(
|
||||
if (normalized === "admin") {
|
||||
const adm =
|
||||
adminSection || adminSectionFromPath(window.location.pathname, routePrefix) || "stats";
|
||||
const uid =
|
||||
adminUserId ??
|
||||
(adm === "users" ? adminUserIdFromPath(window.location.pathname, routePrefix) : null);
|
||||
const clearAdminUser = adminUserId === 0 || adminUserId === false;
|
||||
const uid = clearAdminUser
|
||||
? null
|
||||
: (adminUserId ??
|
||||
(adm === "users" ? adminUserIdFromPath(window.location.pathname, routePrefix) : null));
|
||||
const supportTicketId =
|
||||
adm === "support"
|
||||
? adminSupportTicketIdFromPath(window.location.pathname, routePrefix)
|
||||
@@ -130,7 +132,7 @@ export function syncSectionPath(
|
||||
const paymentId =
|
||||
adm === "payments" ? adminPaymentIdFromPath(window.location.pathname, routePrefix) : null;
|
||||
const paymentUserId =
|
||||
adm === "payments"
|
||||
adm === "payments" && !clearAdminUser
|
||||
? adminPaymentsUserIdFromPath(window.location.pathname, routePrefix)
|
||||
: null;
|
||||
if (adm === "users" && uid) targetPath = `/admin/users/${uid}`;
|
||||
|
||||
@@ -603,6 +603,26 @@
|
||||
"overridden": false,
|
||||
"updated_at": null
|
||||
},
|
||||
{
|
||||
"key": "PAYMENT_REQUEST_TIMEOUT_SECONDS",
|
||||
"type": "float",
|
||||
"section": "payments",
|
||||
"section_order": 4,
|
||||
"subsection": "checkout",
|
||||
"label": "Таймаут запроса к провайдеру",
|
||||
"description": "Максимальное общее время одного API-запроса к платёжному провайдеру, в секундах.",
|
||||
"i18n_label_key": "admin_settings_field_payment_request_timeout_seconds_label",
|
||||
"i18n_description_key": "admin_settings_field_payment_request_timeout_seconds_description",
|
||||
"i18n_subsection_key": "admin_settings_subsection_checkout",
|
||||
"i18n_placeholder_key": null,
|
||||
"placeholder": "",
|
||||
"optional": false,
|
||||
"secret": false,
|
||||
"min": 1,
|
||||
"value": "",
|
||||
"overridden": false,
|
||||
"updated_at": null
|
||||
},
|
||||
{
|
||||
"key": "STARS_ENABLED",
|
||||
"type": "bool",
|
||||
|
||||
Reference in New Issue
Block a user