h5先关
This commit is contained in:
parent
3fa9222b99
commit
9a9dfce9ba
@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
|
||||
<title>Manager Center</title>
|
||||
<link rel="stylesheet" href="./style.css?v=20260624-0100" />
|
||||
<link rel="stylesheet" href="./style.css?v=20260625-0100" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="manager-center" aria-label="Manager Center" data-i18n-aria="page_label" data-loading="true">
|
||||
@ -39,35 +39,8 @@
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card search-card">
|
||||
<div class="section-head">
|
||||
<h2 data-i18n="user">User</h2>
|
||||
</div>
|
||||
<form class="search-form" id="searchForm" autocomplete="off">
|
||||
<input id="accountInput" type="text" inputmode="text" placeholder="Enter user ID or account" data-i18n-placeholder="user_search_placeholder" />
|
||||
<button id="searchButton" type="submit" data-i18n="search">Search</button>
|
||||
</form>
|
||||
<div class="form-status" id="searchStatus" role="status" hidden></div>
|
||||
</section>
|
||||
|
||||
<section class="card target-card" id="targetCard" hidden>
|
||||
<div class="avatar-shell">
|
||||
<img id="targetAvatar" alt="" hidden />
|
||||
<span id="targetAvatarFallback">U</span>
|
||||
</div>
|
||||
<div class="profile-copy">
|
||||
<div class="eyebrow" data-i18n="target_user">Target User</div>
|
||||
<div class="name" id="targetName">-</div>
|
||||
<div class="meta" id="targetUid">UID: -</div>
|
||||
<div class="pill-row">
|
||||
<span class="status-pill" id="targetAccountStatus">-</span>
|
||||
<span class="status-pill" id="targetCountryStatus">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="card action-card" id="actionCard" hidden>
|
||||
<button class="action-item" id="addBDLeaderButton" type="button" disabled>
|
||||
<section class="card action-card" id="actionCard" aria-label="Manager actions">
|
||||
<button class="action-item" id="addBDLeaderButton" type="button">
|
||||
<span class="action-item-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H6a4 4 0 0 0-4 4v2" />
|
||||
@ -83,7 +56,7 @@
|
||||
<path d="m9 6 6 6-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="action-item" id="countryEntryButton" type="button" disabled>
|
||||
<button class="action-item" id="countryEntryButton" type="button">
|
||||
<span class="action-item-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
@ -116,7 +89,7 @@
|
||||
<path d="m9 6 6 6-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="action-item is-danger" id="banEntryButton" type="button" disabled>
|
||||
<button class="action-item is-danger" id="banEntryButton" type="button">
|
||||
<span class="action-item-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<circle cx="12" cy="12" r="9" />
|
||||
@ -130,7 +103,7 @@
|
||||
<path d="m9 6 6 6-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="action-item" id="unbanEntryButton" type="button" disabled>
|
||||
<button class="action-item" id="unbanEntryButton" type="button">
|
||||
<span class="action-item-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M12 3 20 7v5c0 5-3.4 8-8 9-4.6-1-8-4-8-9V7l8-4Z" />
|
||||
@ -144,25 +117,31 @@
|
||||
<path d="m9 6 6 6-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
<button class="action-item" id="bdLeaderListButton" type="button">
|
||||
<span class="action-item-icon" aria-hidden="true">
|
||||
<svg viewBox="0 0 24 24">
|
||||
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 0 0-3-3.87" />
|
||||
<path d="M16 3.13a4 4 0 0 1 0 7.75" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="action-item-copy">
|
||||
<span class="action-item-title" data-i18n="bd_leaders">BD Leaders</span>
|
||||
<span class="action-item-subtitle" id="bdLeaderSummary">0</span>
|
||||
</span>
|
||||
<svg class="action-item-arrow" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="m9 6 6 6-6 6" />
|
||||
</svg>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<section class="card bd-leader-card">
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 data-i18n="bd_leaders">BD Leaders</h2>
|
||||
<div class="section-subtitle" id="bdLeaderCount">0</div>
|
||||
</div>
|
||||
<button class="text-button" id="refreshBDLeaderButton" type="button" data-i18n="refresh">Refresh</button>
|
||||
</div>
|
||||
<div class="form-status" id="bdLeaderStatus" role="status" hidden></div>
|
||||
<div class="bd-leader-list" id="bdLeaderList"></div>
|
||||
</section>
|
||||
<div class="form-status" id="pageStatus" role="status" hidden></div>
|
||||
</main>
|
||||
|
||||
<div class="modal-mask" id="countryModal" role="dialog" aria-modal="true" aria-labelledby="countryModalTitle" hidden>
|
||||
<div class="modal-mask" id="actionModal" role="dialog" aria-modal="true" aria-labelledby="actionModalTitle" hidden>
|
||||
<section class="modal-sheet">
|
||||
<div class="modal-head">
|
||||
<h2 id="countryModalTitle" data-i18n="change_country">Change Country</h2>
|
||||
<h2 id="actionModalTitle">-</h2>
|
||||
<button class="modal-close" type="button" data-modal-close aria-label="Close" data-i18n-aria="close">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M6 6 18 18M18 6 6 18" />
|
||||
@ -170,106 +149,111 @@
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="field-label" id="countryLabelText" data-i18n="select_country">Select country</label>
|
||||
<div class="current-country" id="currentCountryText">-</div>
|
||||
<div class="custom-select" id="countrySelect" data-value="">
|
||||
<button
|
||||
class="custom-select-button"
|
||||
id="countryButton"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-labelledby="countryLabelText countrySelectedLabel"
|
||||
>
|
||||
<span id="countrySelectedLabel">-</span>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="m7 9 5 5 5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="custom-select-menu" id="countryMenu" role="listbox" aria-labelledby="countryLabelText" hidden></div>
|
||||
</div>
|
||||
<button class="primary-action" id="changeCountryButton" type="button" disabled data-i18n="change_country">Change Country</button>
|
||||
<div class="form-status" id="countryStatus" role="status" hidden></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<form class="search-form modal-search-form" id="modalSearchForm" autocomplete="off">
|
||||
<input id="modalAccountInput" type="text" inputmode="text" placeholder="Enter user ID or account" data-i18n-placeholder="user_search_placeholder" />
|
||||
<button id="modalSearchButton" type="submit" data-i18n="search">Search</button>
|
||||
</form>
|
||||
<div class="form-status" id="modalSearchStatus" role="status" hidden></div>
|
||||
|
||||
<div class="modal-mask" id="giftModal" role="dialog" aria-modal="true" aria-labelledby="giftModalTitle" hidden>
|
||||
<section class="modal-sheet">
|
||||
<div class="modal-head">
|
||||
<h2 id="giftModalTitle" data-i18n="gift_props">Gift Props</h2>
|
||||
<button class="modal-close" type="button" data-modal-close aria-label="Close" data-i18n-aria="close">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M6 6 18 18M18 6 6 18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<label class="field-label" id="propsTypeLabelText" data-i18n="gift_type">Gift Type</label>
|
||||
<div class="custom-select" id="propsTypeSelect" data-value="RIDE">
|
||||
<button
|
||||
class="custom-select-button"
|
||||
id="propsTypeButton"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-labelledby="propsTypeLabelText propsTypeSelectedLabel"
|
||||
>
|
||||
<span id="propsTypeSelectedLabel">Ride</span>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="m7 9 5 5 5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="custom-select-menu" id="propsTypeMenu" role="listbox" aria-labelledby="propsTypeLabelText" hidden>
|
||||
<button type="button" role="option" data-value="RIDE" data-i18n="prop_type_ride">Ride</button>
|
||||
<button type="button" role="option" data-value="NOBLE_VIP" data-i18n="prop_type_noble_vip">Noble VIP</button>
|
||||
<button type="button" role="option" data-value="AVATAR_FRAME" data-i18n="prop_type_avatar_frame">Avatar Frame</button>
|
||||
<button type="button" role="option" data-value="BADGE" data-i18n="prop_type_badge">Badge</button>
|
||||
<button type="button" role="option" data-value="CHAT_BUBBLE" data-i18n="prop_type_chat_bubble">Chat Bubble</button>
|
||||
<button type="button" role="option" data-value="DATA_CARD" data-i18n="prop_type_data_card">Profile Card</button>
|
||||
<section class="target-card modal-target-card" id="targetCard" hidden>
|
||||
<div class="avatar-shell">
|
||||
<img id="targetAvatar" alt="" hidden />
|
||||
<span id="targetAvatarFallback">U</span>
|
||||
</div>
|
||||
<div class="profile-copy">
|
||||
<div class="eyebrow" data-i18n="target_user">Target User</div>
|
||||
<div class="name" id="targetName">-</div>
|
||||
<div class="meta" id="targetUid">UID: -</div>
|
||||
<div class="pill-row">
|
||||
<span class="status-pill" id="targetAccountStatus">-</span>
|
||||
<span class="status-pill" id="targetCountryStatus">-</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="operation-panel" id="addBDLeaderPanel" hidden>
|
||||
<button class="primary-action" id="addBDLeaderConfirmButton" type="button" disabled data-i18n="add_bd_leader">Add BD Leader</button>
|
||||
<div class="form-status" id="addBDLeaderStatus" role="status" hidden></div>
|
||||
</div>
|
||||
<div class="props-status" id="propsStatus" role="status" hidden></div>
|
||||
<div class="props-list" id="propsList"></div>
|
||||
<button class="primary-action" id="sendPropsButton" type="button" disabled data-i18n="send">Send</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<div class="modal-mask" id="banModal" role="dialog" aria-modal="true" aria-labelledby="banModalTitle" hidden>
|
||||
<section class="modal-sheet">
|
||||
<div class="modal-head">
|
||||
<h2 id="banModalTitle" data-i18n="ban_user">Ban User</h2>
|
||||
<button class="modal-close" type="button" data-modal-close aria-label="Close" data-i18n-aria="close">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M6 6 18 18M18 6 6 18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="danger-chip" id="banEligibility">-</span>
|
||||
<textarea id="banReason" maxlength="200" rows="3" placeholder="Ban reason" data-i18n-placeholder="ban_reason"></textarea>
|
||||
<button class="danger-action" id="banButton" type="button" disabled data-i18n="ban">Ban</button>
|
||||
<div class="form-status" id="banStatus" role="status" hidden></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<div class="operation-panel" id="countryPanel" hidden>
|
||||
<label class="field-label" id="countryLabelText" data-i18n="select_country">Select country</label>
|
||||
<div class="current-country" id="currentCountryText">-</div>
|
||||
<div class="custom-select" id="countrySelect" data-value="">
|
||||
<button
|
||||
class="custom-select-button"
|
||||
id="countryButton"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-labelledby="countryLabelText countrySelectedLabel"
|
||||
>
|
||||
<span id="countrySelectedLabel">-</span>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="m7 9 5 5 5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="custom-select-menu" id="countryMenu" role="listbox" aria-labelledby="countryLabelText" hidden></div>
|
||||
</div>
|
||||
<button class="primary-action" id="changeCountryButton" type="button" disabled data-i18n="change_country">Change Country</button>
|
||||
<div class="form-status" id="countryStatus" role="status" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="modal-mask" id="unbanModal" role="dialog" aria-modal="true" aria-labelledby="unbanModalTitle" hidden>
|
||||
<section class="modal-sheet">
|
||||
<div class="modal-head">
|
||||
<h2 id="unbanModalTitle" data-i18n="unban_user">Unban User</h2>
|
||||
<button class="modal-close" type="button" data-modal-close aria-label="Close" data-i18n-aria="close">
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="M6 6 18 18M18 6 6 18" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<span class="danger-chip" id="unbanEligibility">-</span>
|
||||
<textarea id="unbanReason" maxlength="200" rows="3" placeholder="Unban reason" data-i18n-placeholder="unban_reason"></textarea>
|
||||
<button class="primary-action" id="unbanButton" type="button" disabled data-i18n="unban">Unban</button>
|
||||
<div class="form-status" id="unbanStatus" role="status" hidden></div>
|
||||
<div class="operation-panel" id="giftPanel" hidden>
|
||||
<label class="field-label" id="propsTypeLabelText" data-i18n="gift_type">Gift Type</label>
|
||||
<div class="custom-select" id="propsTypeSelect" data-value="RIDE">
|
||||
<button
|
||||
class="custom-select-button"
|
||||
id="propsTypeButton"
|
||||
type="button"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-labelledby="propsTypeLabelText propsTypeSelectedLabel"
|
||||
>
|
||||
<span id="propsTypeSelectedLabel">Ride</span>
|
||||
<svg viewBox="0 0 24 24" aria-hidden="true">
|
||||
<path d="m7 9 5 5 5-5" />
|
||||
</svg>
|
||||
</button>
|
||||
<div class="custom-select-menu" id="propsTypeMenu" role="listbox" aria-labelledby="propsTypeLabelText" hidden>
|
||||
<button type="button" role="option" data-value="RIDE" data-i18n="prop_type_ride">Ride</button>
|
||||
<button type="button" role="option" data-value="NOBLE_VIP" data-i18n="prop_type_noble_vip">Noble VIP</button>
|
||||
<button type="button" role="option" data-value="AVATAR_FRAME" data-i18n="prop_type_avatar_frame">Avatar Frame</button>
|
||||
<button type="button" role="option" data-value="BADGE" data-i18n="prop_type_badge">Badge</button>
|
||||
<button type="button" role="option" data-value="CHAT_BUBBLE" data-i18n="prop_type_chat_bubble">Chat Bubble</button>
|
||||
<button type="button" role="option" data-value="DATA_CARD" data-i18n="prop_type_data_card">Profile Card</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="props-status" id="propsStatus" role="status" hidden></div>
|
||||
<div class="props-list" id="propsList"></div>
|
||||
<button class="primary-action" id="sendPropsButton" type="button" disabled data-i18n="send">Send</button>
|
||||
</div>
|
||||
|
||||
<div class="operation-panel" id="banPanel" hidden>
|
||||
<span class="danger-chip" id="banEligibility">-</span>
|
||||
<textarea id="banReason" maxlength="200" rows="3" placeholder="Ban reason" data-i18n-placeholder="ban_reason"></textarea>
|
||||
<button class="danger-action" id="banButton" type="button" disabled data-i18n="ban">Ban</button>
|
||||
<div class="form-status" id="banStatus" role="status" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="operation-panel" id="unbanPanel" hidden>
|
||||
<span class="danger-chip" id="unbanEligibility">-</span>
|
||||
<textarea id="unbanReason" maxlength="200" rows="3" placeholder="Unban reason" data-i18n-placeholder="unban_reason"></textarea>
|
||||
<button class="primary-action" id="unbanButton" type="button" disabled data-i18n="unban">Unban</button>
|
||||
<div class="form-status" id="unbanStatus" role="status" hidden></div>
|
||||
</div>
|
||||
|
||||
<div class="operation-panel" id="bdLeaderPanel" hidden>
|
||||
<div class="section-head">
|
||||
<div>
|
||||
<h2 data-i18n="bd_leaders">BD Leaders</h2>
|
||||
<div class="section-subtitle" id="bdLeaderCount">0</div>
|
||||
</div>
|
||||
<button class="text-button" id="refreshBDLeaderButton" type="button" data-i18n="refresh">Refresh</button>
|
||||
</div>
|
||||
<div class="form-status" id="bdLeaderStatus" role="status" hidden></div>
|
||||
<div class="bd-leader-list" id="bdLeaderList"></div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
@ -280,6 +264,6 @@
|
||||
</div>
|
||||
<div class="toast" id="toast" role="status" hidden></div>
|
||||
</div>
|
||||
<script src="./script.js?v=20260624-0100" defer></script>
|
||||
<script src="./script.js?v=20260625-0100" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -349,6 +349,7 @@
|
||||
const state = {
|
||||
manager: null,
|
||||
target: null,
|
||||
activeAction: "",
|
||||
features: defaultFeatures(),
|
||||
propsType: defaultPropsType,
|
||||
props: [],
|
||||
@ -363,6 +364,54 @@
|
||||
addingBDLeader: false
|
||||
};
|
||||
|
||||
const actionConfigs = {
|
||||
addBDLeader: {
|
||||
titleKey: "add_bd_leader",
|
||||
disabledKey: "add_bd_leader_disabled",
|
||||
feature: "addBdLeader",
|
||||
panel: "#addBDLeaderPanel",
|
||||
status: "#addBDLeaderStatus",
|
||||
needsUser: true
|
||||
},
|
||||
changeCountry: {
|
||||
titleKey: "change_country",
|
||||
disabledKey: "change_country_disabled",
|
||||
feature: "changeCountry",
|
||||
panel: "#countryPanel",
|
||||
status: "#countryStatus",
|
||||
needsUser: true
|
||||
},
|
||||
giftProps: {
|
||||
titleKey: "gift_props",
|
||||
disabledKey: "gift_props_disabled",
|
||||
feature: "giftProps",
|
||||
panel: "#giftPanel",
|
||||
status: "#propsStatus",
|
||||
needsUser: true
|
||||
},
|
||||
banUser: {
|
||||
titleKey: "ban_user",
|
||||
disabledKey: "ban_user_disabled",
|
||||
feature: "banUser",
|
||||
panel: "#banPanel",
|
||||
status: "#banStatus",
|
||||
needsUser: true
|
||||
},
|
||||
unbanUser: {
|
||||
titleKey: "unban_user",
|
||||
disabledKey: "unban_user_disabled",
|
||||
feature: "unbanUser",
|
||||
panel: "#unbanPanel",
|
||||
status: "#unbanStatus",
|
||||
needsUser: true
|
||||
},
|
||||
bdLeaders: {
|
||||
titleKey: "bd_leaders",
|
||||
panel: "#bdLeaderPanel",
|
||||
needsUser: false
|
||||
}
|
||||
};
|
||||
|
||||
const params = readURLParams();
|
||||
let currentLang = normalizeLanguage(params.get("lang"));
|
||||
let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
|
||||
@ -414,7 +463,7 @@
|
||||
if (window.location.protocol === "file:") return fallbackMessages[normalizedLang];
|
||||
|
||||
try {
|
||||
const response = await fetch(`./locales/${normalizedLang}.json?v=20260624-0100`, { cache: "no-store" });
|
||||
const response = await fetch(`./locales/${normalizedLang}.json?v=20260625-0100`, { cache: "no-store" });
|
||||
if (response.ok) return await response.json();
|
||||
} catch (error) {
|
||||
return fallbackMessages[normalizedLang];
|
||||
@ -570,6 +619,7 @@
|
||||
renderCountrySelect();
|
||||
renderManager(state.manager);
|
||||
renderTarget();
|
||||
renderActionModal();
|
||||
renderProps();
|
||||
renderBDLeaders();
|
||||
updateActionState();
|
||||
@ -723,6 +773,47 @@
|
||||
document.body.classList.toggle("is-modal-open", Boolean(document.querySelector(".modal-mask:not([hidden])")));
|
||||
}
|
||||
|
||||
function activeConfig() {
|
||||
return actionConfigs[state.activeAction] || null;
|
||||
}
|
||||
|
||||
function clearActionStatuses() {
|
||||
setStatus($("#modalSearchStatus"), "");
|
||||
Object.values(actionConfigs).forEach((config) => {
|
||||
if (config.status) setStatus($(config.status), "");
|
||||
});
|
||||
}
|
||||
|
||||
function clearActionModalForm() {
|
||||
const input = $("#modalAccountInput");
|
||||
if (input) input.value = "";
|
||||
const banReason = $("#banReason");
|
||||
const unbanReason = $("#unbanReason");
|
||||
if (banReason) banReason.value = "";
|
||||
if (unbanReason) unbanReason.value = "";
|
||||
state.target = null;
|
||||
state.selectedCountryId = "";
|
||||
renderTarget();
|
||||
clearActionStatuses();
|
||||
}
|
||||
|
||||
function renderActionModal() {
|
||||
const config = activeConfig();
|
||||
const title = $("#actionModalTitle");
|
||||
if (title) title.textContent = config ? message(config.titleKey) : "-";
|
||||
|
||||
const needsUser = config?.needsUser !== false;
|
||||
const searchForm = $("#modalSearchForm");
|
||||
const modalTarget = $("#targetCard");
|
||||
if (searchForm) searchForm.hidden = !needsUser;
|
||||
if (modalTarget && !needsUser) modalTarget.hidden = true;
|
||||
|
||||
Object.values(actionConfigs).forEach((item) => {
|
||||
const panel = $(item.panel);
|
||||
if (panel) panel.hidden = item !== config || (item.needsUser !== false && !state.target);
|
||||
});
|
||||
}
|
||||
|
||||
function setModalOpen(selector, open) {
|
||||
const modal = $(selector);
|
||||
if (!modal) return;
|
||||
@ -739,77 +830,56 @@
|
||||
document.querySelectorAll(".modal-mask").forEach((modal) => {
|
||||
modal.hidden = true;
|
||||
});
|
||||
state.activeAction = "";
|
||||
state.target = null;
|
||||
setPropsTypeMenuOpen(false);
|
||||
setCountryMenuOpen(false);
|
||||
renderTarget();
|
||||
syncModalState();
|
||||
}
|
||||
|
||||
function openGiftModal() {
|
||||
if (!featureEnabled("giftProps")) {
|
||||
showToast(message("gift_props_disabled"));
|
||||
function openActionModal(action) {
|
||||
const config = actionConfigs[action];
|
||||
if (!config) return;
|
||||
if (config.feature && !featureEnabled(config.feature)) {
|
||||
showToast(message(config.disabledKey));
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
showToast(message("enter_user"));
|
||||
return;
|
||||
}
|
||||
setModalOpen("#giftModal", true);
|
||||
|
||||
state.activeAction = action;
|
||||
clearActionModalForm();
|
||||
renderActionModal();
|
||||
setModalOpen("#actionModal", true);
|
||||
updateActionState();
|
||||
|
||||
if (action === "giftProps" && !state.props.length) {
|
||||
loadProps().catch((error) => {
|
||||
setStatus($("#propsStatus"), friendlyError(error), "error");
|
||||
});
|
||||
}
|
||||
if (action === "bdLeaders") {
|
||||
renderBDLeaders();
|
||||
}
|
||||
|
||||
window.setTimeout(() => {
|
||||
if (config.needsUser !== false) $("#modalAccountInput")?.focus();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
async function openCountryModal() {
|
||||
if (!featureEnabled("changeCountry")) {
|
||||
showToast(message("change_country_disabled"));
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
showToast(message("enter_user"));
|
||||
return;
|
||||
}
|
||||
function openGiftModal() {
|
||||
openActionModal("giftProps");
|
||||
}
|
||||
|
||||
setStatus($("#countryStatus"), "");
|
||||
const currentCountryText = $("#currentCountryText");
|
||||
if (currentCountryText) currentCountryText.textContent = `${message("current_country")}: ${displayCountryText(state.target)}`;
|
||||
state.selectedCountryId = String(state.target.countryId || matchingCountryId(state.target) || "");
|
||||
setModalOpen("#countryModal", true);
|
||||
renderCountrySelect();
|
||||
updateActionState();
|
||||
|
||||
if (!state.countries.length) {
|
||||
try {
|
||||
await loadCountries();
|
||||
} catch (error) {
|
||||
setStatus($("#countryStatus"), friendlyError(error), "error");
|
||||
}
|
||||
}
|
||||
function openCountryModal() {
|
||||
openActionModal("changeCountry");
|
||||
}
|
||||
|
||||
function openBanModal() {
|
||||
if (!featureEnabled("banUser")) {
|
||||
showToast(message("ban_user_disabled"));
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
showToast(message("enter_user"));
|
||||
return;
|
||||
}
|
||||
setStatus($("#banStatus"), "");
|
||||
setModalOpen("#banModal", true);
|
||||
updateActionState();
|
||||
openActionModal("banUser");
|
||||
}
|
||||
|
||||
function openUnbanModal() {
|
||||
if (!featureEnabled("unbanUser")) {
|
||||
showToast(message("unban_user_disabled"));
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
showToast(message("enter_user"));
|
||||
return;
|
||||
}
|
||||
setStatus($("#unbanStatus"), "");
|
||||
setModalOpen("#unbanModal", true);
|
||||
updateActionState();
|
||||
openActionModal("unbanUser");
|
||||
}
|
||||
|
||||
function closePage() {
|
||||
@ -943,6 +1013,13 @@
|
||||
return message("bd_leader_count", "{count} BD Leaders").replace("{count}", new Intl.NumberFormat("en-US").format(Number(count) || 0));
|
||||
}
|
||||
|
||||
function formatBDLeaderSummary() {
|
||||
const countText = formatBDLeaderCount(state.bdLeaders.length);
|
||||
const firstLeader = state.bdLeaders[0];
|
||||
if (!firstLeader) return countText;
|
||||
return `${countText} - ${userIDText(firstLeader)}`;
|
||||
}
|
||||
|
||||
function formatDateTime(value) {
|
||||
if (!value) return "";
|
||||
const date = new Date(value);
|
||||
@ -973,18 +1050,15 @@
|
||||
|
||||
function renderTarget() {
|
||||
const card = $("#targetCard");
|
||||
const actionCard = $("#actionCard");
|
||||
if (!state.target) {
|
||||
if (card) card.hidden = true;
|
||||
if (actionCard) actionCard.hidden = true;
|
||||
closeModals();
|
||||
renderActionModal();
|
||||
updateActionState();
|
||||
return;
|
||||
}
|
||||
|
||||
const target = state.target;
|
||||
if (card) card.hidden = false;
|
||||
if (actionCard) actionCard.hidden = false;
|
||||
$("#targetName").textContent = displayName(target);
|
||||
$("#targetUid").textContent = userIDText(target);
|
||||
$("#targetAccountStatus").textContent = target.accountStatus || "-";
|
||||
@ -1002,6 +1076,28 @@
|
||||
unbanEligibility.classList.toggle("is-danger", !canUnban);
|
||||
}
|
||||
renderAvatar("#targetAvatar", "#targetAvatarFallback", target.userAvatar, displayName(target), "U");
|
||||
renderActionModal();
|
||||
updateActionState();
|
||||
}
|
||||
|
||||
async function prepareActiveActionAfterTarget() {
|
||||
if (!state.target) return;
|
||||
|
||||
if (state.activeAction === "changeCountry") {
|
||||
const currentCountryText = $("#currentCountryText");
|
||||
if (currentCountryText) currentCountryText.textContent = `${message("current_country")}: ${displayCountryText(state.target)}`;
|
||||
state.selectedCountryId = String(state.target.countryId || matchingCountryId(state.target) || "");
|
||||
renderCountrySelect();
|
||||
if (!state.countries.length) {
|
||||
await loadCountries();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.activeAction === "giftProps") {
|
||||
renderProps();
|
||||
}
|
||||
|
||||
updateActionState();
|
||||
}
|
||||
|
||||
@ -1071,7 +1167,10 @@
|
||||
function renderBDLeaders() {
|
||||
const list = $("#bdLeaderList");
|
||||
const count = $("#bdLeaderCount");
|
||||
if (count) count.textContent = formatBDLeaderCount(state.bdLeaders.length);
|
||||
const summary = $("#bdLeaderSummary");
|
||||
const leaderCountText = formatBDLeaderCount(state.bdLeaders.length);
|
||||
if (count) count.textContent = leaderCountText;
|
||||
if (summary) summary.textContent = formatBDLeaderSummary();
|
||||
if (!list) return;
|
||||
|
||||
list.replaceChildren();
|
||||
@ -1116,10 +1215,7 @@
|
||||
name.textContent = displayName(leader);
|
||||
const meta = document.createElement("span");
|
||||
meta.className = "bd-leader-meta";
|
||||
const metaItems = [userIDText(leader)];
|
||||
if (leader.regionId) metaItems.push(leader.regionId);
|
||||
if (leader.originSys) metaItems.push(leader.originSys);
|
||||
meta.textContent = metaItems.join(" · ");
|
||||
meta.textContent = userIDText(leader);
|
||||
copy.append(name, meta);
|
||||
|
||||
const time = document.createElement("span");
|
||||
@ -1138,14 +1234,16 @@
|
||||
const giftEntryButton = $("#giftEntryButton");
|
||||
const banEntryButton = $("#banEntryButton");
|
||||
const unbanEntryButton = $("#unbanEntryButton");
|
||||
const bdLeaderListButton = $("#bdLeaderListButton");
|
||||
const refreshBDLeaderButton = $("#refreshBDLeaderButton");
|
||||
const addBDLeaderConfirmButton = $("#addBDLeaderConfirmButton");
|
||||
const sendButton = $("#sendPropsButton");
|
||||
const changeCountryButton = $("#changeCountryButton");
|
||||
const banButton = $("#banButton");
|
||||
const unbanButton = $("#unbanButton");
|
||||
if (addBDLeaderButton) {
|
||||
const disabledByFeature = !featureEnabled("addBdLeader");
|
||||
addBDLeaderButton.disabled = disabledByFeature || !state.target || state.addingBDLeader || isTargetBDLeader();
|
||||
addBDLeaderButton.disabled = disabledByFeature || state.addingBDLeader;
|
||||
addBDLeaderButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||||
const title = addBDLeaderButton.querySelector(".action-item-title");
|
||||
if (title) {
|
||||
@ -1156,33 +1254,38 @@
|
||||
}
|
||||
if (giftEntryButton) {
|
||||
const disabledByFeature = !featureEnabled("giftProps");
|
||||
giftEntryButton.disabled = disabledByFeature || !state.target;
|
||||
giftEntryButton.disabled = disabledByFeature;
|
||||
giftEntryButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||||
const title = giftEntryButton.querySelector(".action-item-title");
|
||||
if (title) title.textContent = disabledByFeature ? message("gift_props_disabled") : message("gift_props");
|
||||
}
|
||||
if (countryEntryButton) {
|
||||
const disabledByFeature = !featureEnabled("changeCountry");
|
||||
countryEntryButton.disabled = disabledByFeature || !state.target || state.countriesLoading || state.changingCountry;
|
||||
countryEntryButton.disabled = disabledByFeature || state.changingCountry;
|
||||
countryEntryButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||||
const title = countryEntryButton.querySelector(".action-item-title");
|
||||
if (title) title.textContent = disabledByFeature ? message("change_country_disabled") : message("change_country");
|
||||
}
|
||||
if (banEntryButton) {
|
||||
const disabledByFeature = !featureEnabled("banUser");
|
||||
banEntryButton.disabled = disabledByFeature || !state.target;
|
||||
banEntryButton.disabled = disabledByFeature;
|
||||
banEntryButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||||
const title = banEntryButton.querySelector(".action-item-title");
|
||||
if (title) title.textContent = disabledByFeature ? message("ban_user_disabled") : message("ban_user");
|
||||
}
|
||||
if (unbanEntryButton) {
|
||||
const disabledByFeature = !featureEnabled("unbanUser");
|
||||
unbanEntryButton.disabled = disabledByFeature || !state.target;
|
||||
unbanEntryButton.disabled = disabledByFeature;
|
||||
unbanEntryButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||||
const title = unbanEntryButton.querySelector(".action-item-title");
|
||||
if (title) title.textContent = disabledByFeature ? message("unban_user_disabled") : message("unban_user");
|
||||
}
|
||||
if (bdLeaderListButton) bdLeaderListButton.disabled = state.bdLeadersLoading;
|
||||
if (refreshBDLeaderButton) refreshBDLeaderButton.disabled = state.bdLeadersLoading;
|
||||
if (addBDLeaderConfirmButton) {
|
||||
addBDLeaderConfirmButton.disabled = !featureEnabled("addBdLeader") || !state.target || state.addingBDLeader || isTargetBDLeader();
|
||||
addBDLeaderConfirmButton.textContent = state.addingBDLeader ? message("adding_bd_leader") : message("add_bd_leader");
|
||||
}
|
||||
if (sendButton) sendButton.disabled = !featureEnabled("giftProps") || !state.target || !state.selectedPropsId;
|
||||
if (changeCountryButton) {
|
||||
const currentID = String(state.target?.countryId || matchingCountryId(state.target) || "");
|
||||
@ -1252,26 +1355,29 @@
|
||||
|
||||
async function searchUser(event) {
|
||||
event.preventDefault();
|
||||
const input = $("#accountInput");
|
||||
const input = $("#modalAccountInput");
|
||||
const status = $("#modalSearchStatus");
|
||||
const button = $("#modalSearchButton");
|
||||
const account = String(input?.value || "").trim();
|
||||
if (!account) {
|
||||
setStatus($("#searchStatus"), message("enter_user"), "error");
|
||||
setStatus(status, message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
$("#searchButton").disabled = true;
|
||||
setStatus($("#searchStatus"), message("searching"));
|
||||
button.disabled = true;
|
||||
setStatus(status, message("searching"));
|
||||
try {
|
||||
const body = await requestGoJSON(endpointSearchUser(account));
|
||||
state.target = body?.user || body || null;
|
||||
renderTarget();
|
||||
setStatus($("#searchStatus"), "");
|
||||
await prepareActiveActionAfterTarget();
|
||||
setStatus(status, "");
|
||||
} catch (error) {
|
||||
state.target = null;
|
||||
renderTarget();
|
||||
setStatus($("#searchStatus"), friendlyError(error), "error");
|
||||
setStatus(status, friendlyError(error), "error");
|
||||
} finally {
|
||||
$("#searchButton").disabled = false;
|
||||
button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1280,13 +1386,18 @@
|
||||
showToast(message("add_bd_leader_disabled"));
|
||||
return;
|
||||
}
|
||||
if (!state.target || state.addingBDLeader) return;
|
||||
if (!state.target) {
|
||||
setStatus($("#addBDLeaderStatus"), message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
if (state.addingBDLeader) return;
|
||||
if (isTargetBDLeader()) {
|
||||
showToast(message("user_already_bd_leader"));
|
||||
setStatus($("#addBDLeaderStatus"), message("user_already_bd_leader"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
state.addingBDLeader = true;
|
||||
setStatus($("#addBDLeaderStatus"), message("adding_bd_leader"));
|
||||
updateActionState();
|
||||
try {
|
||||
await requestGoJSON(endpointBDLeaders(), {
|
||||
@ -1295,10 +1406,12 @@
|
||||
userId: displayUserID(state.target)
|
||||
}
|
||||
});
|
||||
setStatus($("#addBDLeaderStatus"), "");
|
||||
closeModals();
|
||||
showToast(message("add_bd_leader_success"));
|
||||
await loadBDLeaders();
|
||||
} catch (error) {
|
||||
showToast(friendlyError(error));
|
||||
setStatus($("#addBDLeaderStatus"), friendlyError(error), "error");
|
||||
} finally {
|
||||
state.addingBDLeader = false;
|
||||
updateActionState();
|
||||
@ -1310,7 +1423,11 @@
|
||||
showToast(message("gift_props_disabled"));
|
||||
return;
|
||||
}
|
||||
if (!state.target || !state.selectedPropsId) return;
|
||||
if (!state.target) {
|
||||
setStatus($("#propsStatus"), message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
if (!state.selectedPropsId) return;
|
||||
|
||||
const button = $("#sendPropsButton");
|
||||
button.disabled = true;
|
||||
@ -1322,7 +1439,7 @@
|
||||
propsId: state.selectedPropsId
|
||||
}
|
||||
});
|
||||
setModalOpen("#giftModal", false);
|
||||
closeModals();
|
||||
showToast(message("send_success"));
|
||||
} catch (error) {
|
||||
showToast(friendlyError(error));
|
||||
@ -1336,6 +1453,10 @@
|
||||
setStatus($("#banStatus"), message("ban_user_disabled"), "error");
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
setStatus($("#banStatus"), message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
if (!canBanTarget(state.target)) return;
|
||||
|
||||
const button = $("#banButton");
|
||||
@ -1351,8 +1472,8 @@
|
||||
});
|
||||
state.target.accountStatus = "ARCHIVE";
|
||||
renderTarget();
|
||||
setModalOpen("#banModal", false);
|
||||
setStatus($("#banStatus"), "");
|
||||
closeModals();
|
||||
showToast(message("ban_success"));
|
||||
} catch (error) {
|
||||
setStatus($("#banStatus"), friendlyError(error), "error");
|
||||
@ -1366,6 +1487,10 @@
|
||||
setStatus($("#unbanStatus"), message("unban_user_disabled"), "error");
|
||||
return;
|
||||
}
|
||||
if (!state.target) {
|
||||
setStatus($("#unbanStatus"), message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
if (!canUnbanTarget(state.target)) return;
|
||||
|
||||
const button = $("#unbanButton");
|
||||
@ -1381,8 +1506,8 @@
|
||||
});
|
||||
state.target.accountStatus = "NORMAL";
|
||||
renderTarget();
|
||||
setModalOpen("#unbanModal", false);
|
||||
setStatus($("#unbanStatus"), "");
|
||||
closeModals();
|
||||
showToast(message("unban_success"));
|
||||
} catch (error) {
|
||||
setStatus($("#unbanStatus"), friendlyError(error), "error");
|
||||
@ -1392,7 +1517,10 @@
|
||||
}
|
||||
|
||||
async function changeUserCountry() {
|
||||
if (!state.target) return;
|
||||
if (!state.target) {
|
||||
setStatus($("#countryStatus"), message("enter_user"), "error");
|
||||
return;
|
||||
}
|
||||
if (!featureEnabled("changeCountry")) {
|
||||
setStatus($("#countryStatus"), message("change_country_disabled"), "error");
|
||||
return;
|
||||
@ -1415,8 +1543,8 @@
|
||||
});
|
||||
state.target = body?.user || state.target;
|
||||
renderTarget();
|
||||
setModalOpen("#countryModal", false);
|
||||
setStatus($("#countryStatus"), "");
|
||||
closeModals();
|
||||
showToast(message("country_changed"));
|
||||
} catch (error) {
|
||||
setStatus($("#countryStatus"), friendlyError(error), "error");
|
||||
@ -1467,7 +1595,8 @@
|
||||
setPropsTypeMenuOpen(false);
|
||||
setCountryMenuOpen(false);
|
||||
});
|
||||
$("#addBDLeaderButton")?.addEventListener("click", addBDLeader);
|
||||
$("#addBDLeaderButton")?.addEventListener("click", () => openActionModal("addBDLeader"));
|
||||
$("#bdLeaderListButton")?.addEventListener("click", () => openActionModal("bdLeaders"));
|
||||
$("#refreshBDLeaderButton")?.addEventListener("click", () => {
|
||||
loadBDLeaders().catch((error) => {
|
||||
showToast(friendlyError(error));
|
||||
@ -1510,7 +1639,8 @@
|
||||
setCountryMenuOpen(false);
|
||||
closeModals();
|
||||
});
|
||||
$("#searchForm")?.addEventListener("submit", searchUser);
|
||||
$("#modalSearchForm")?.addEventListener("submit", searchUser);
|
||||
$("#addBDLeaderConfirmButton")?.addEventListener("click", addBDLeader);
|
||||
$("#sendPropsButton")?.addEventListener("click", sendProps);
|
||||
$("#banButton")?.addEventListener("click", banUser);
|
||||
$("#unbanButton")?.addEventListener("click", unbanUser);
|
||||
@ -1523,7 +1653,7 @@
|
||||
await applyLanguage(currentLang);
|
||||
if (!readRawParam("token")) {
|
||||
setLoading(false);
|
||||
setStatus($("#searchStatus"), message("missing_token"), "error");
|
||||
setStatus($("#pageStatus"), message("missing_token"), "error");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@ -278,19 +278,22 @@ button:disabled {
|
||||
|
||||
.action-card {
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
padding: 6px;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 42px minmax(0, 1fr) 24px;
|
||||
gap: 10px;
|
||||
grid-template-columns: 44px minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
min-height: 58px;
|
||||
min-height: 112px;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
padding: 12px;
|
||||
background: #fff;
|
||||
color: var(--text);
|
||||
text-align: left;
|
||||
@ -313,8 +316,8 @@ button:disabled {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 8px;
|
||||
background: #e7f8f4;
|
||||
color: var(--primary-dark);
|
||||
@ -338,6 +341,8 @@ button:disabled {
|
||||
|
||||
.action-item-copy {
|
||||
display: grid;
|
||||
grid-column: 1 / -1;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
@ -349,8 +354,19 @@ button:disabled {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-item-subtitle {
|
||||
overflow: hidden;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
font-weight: 800;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.action-item-arrow {
|
||||
justify-self: end;
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 12px;
|
||||
color: #9aa4af;
|
||||
}
|
||||
|
||||
@ -394,6 +410,10 @@ button:disabled {
|
||||
.bd-leader-list {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
max-height: min(54vh, 420px);
|
||||
overflow-y: auto;
|
||||
padding-right: 2px;
|
||||
overscroll-behavior: contain;
|
||||
}
|
||||
|
||||
.bd-leader-row {
|
||||
@ -814,6 +834,11 @@ textarea:focus {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.modal-target-card,
|
||||
.operation-panel {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.modal-body textarea {
|
||||
display: block;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user