986 lines
33 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

(function () {
const supportedLanguages = ["en", "ar", "tr", "id"];
const languageLabels = {
en: "EN",
ar: "AR",
tr: "TR",
id: "ID"
};
const API_BASE_URL = "https://jvapi.haiyihy.com";
const GO_API_PATH_PREFIX = "/go";
const LOCAL_GO_API_BASE_URL = "http://127.0.0.1:1100";
const defaultPropsType = "RIDE";
const fallbackMessages = {
en: {
document_title: "Manager Center",
page_label: "Manager Center",
back: "Back",
title: "Manager Center",
language_button_aria: "Change language",
close: "Close",
current_manager: "Current Manager",
user: "User",
user_search_placeholder: "Enter user ID or account",
search: "Search",
searching: "Searching...",
target_user: "Target User",
gift_props: "Gift Props",
gift_type: "Gift Type",
prop_type_ride: "Ride",
prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Avatar Frame",
prop_type_badge: "Badge",
prop_type_chat_bubble: "Chat Bubble",
prop_type_data_card: "Profile Card",
no_giftable_props: "No giftable props",
send: "Send",
sending: "Sending...",
send_success: "Sent successfully",
ban_user: "Ban User",
ban_reason: "Ban reason",
ban: "Ban",
submitting: "Submitting...",
ban_success: "User banned",
unban_user: "Unban User",
unban_reason: "Unban reason",
unban: "Unban",
unban_success: "User unbanned",
can_unban: "Can unban",
cannot_unban: "Cannot unban",
loading: "Loading...",
uid_label: "UID",
days: "{count} days",
recharged: "Recharged",
not_recharged: "Not recharged",
recharge_unknown: "Recharge unknown",
can_ban: "Can ban",
cannot_ban: "Cannot ban",
missing_token: "Missing token",
not_manager: "Current account is not a manager",
user_not_found: "User not found",
user_recharged: "This user has recharge records and cannot be banned",
user_already_banned: "This user is already banned",
user_not_banned: "This user is not banned",
prop_not_giftable: "This prop is not available for manager gifting",
vip_not_giftable: "This noble prop cannot be gifted",
request_failed: "Request failed",
enter_user: "Enter user ID or account"
},
ar: {
document_title: "مركز المدير",
page_label: "مركز المدير",
back: "رجوع",
title: "مركز المدير",
language_button_aria: "تغيير اللغة",
close: "إغلاق",
current_manager: "المدير الحالي",
user: "المستخدم",
user_search_placeholder: "أدخل معرّف المستخدم أو الحساب",
search: "بحث",
searching: "جار البحث...",
target_user: "المستخدم المستهدف",
gift_props: "إرسال العناصر",
gift_type: "نوع الهدية",
prop_type_ride: "مركبة",
prop_type_noble_vip: "VIP نبيل",
prop_type_avatar_frame: "إطار الصورة",
prop_type_badge: "شارة",
prop_type_chat_bubble: "فقاعة الدردشة",
prop_type_data_card: "بطاقة الملف",
no_giftable_props: "لا توجد عناصر قابلة للإهداء",
send: "إرسال",
sending: "جار الإرسال...",
send_success: "تم الإرسال بنجاح",
ban_user: "حظر المستخدم",
ban_reason: "سبب الحظر",
ban: "حظر",
submitting: "جار الإرسال...",
ban_success: "تم حظر المستخدم",
unban_user: "إلغاء حظر المستخدم",
unban_reason: "سبب إلغاء الحظر",
unban: "إلغاء الحظر",
unban_success: "تم إلغاء حظر المستخدم",
can_unban: "يمكن إلغاء الحظر",
cannot_unban: "لا يمكن إلغاء الحظر",
loading: "جار التحميل...",
uid_label: "UID",
days: "{count} يوم",
recharged: "تم الشحن",
not_recharged: "لم يتم الشحن",
recharge_unknown: "حالة الشحن غير معروفة",
can_ban: "يمكن الحظر",
cannot_ban: "لا يمكن الحظر",
missing_token: "رمز الدخول مفقود",
not_manager: "الحساب الحالي ليس مديرًا",
user_not_found: "المستخدم غير موجود",
user_recharged: "لدى هذا المستخدم سجلات شحن ولا يمكن حظره",
user_already_banned: "هذا المستخدم محظور بالفعل",
user_not_banned: "هذا المستخدم غير محظور",
prop_not_giftable: "هذا العنصر غير متاح كهديّة من المدير",
vip_not_giftable: "لا يمكن إهداء عنصر VIP هذا",
request_failed: "فشل الطلب",
enter_user: "أدخل معرّف المستخدم أو الحساب"
},
tr: {
document_title: "Yönetici Merkezi",
page_label: "Yönetici Merkezi",
back: "Geri",
title: "Yönetici Merkezi",
language_button_aria: "Dili değiştir",
close: "Kapat",
current_manager: "Mevcut Yönetici",
user: "Kullanıcı",
user_search_placeholder: "Kullanıcı ID veya hesap gir",
search: "Ara",
searching: "Aranıyor...",
target_user: "Hedef Kullanıcı",
gift_props: "Eşya Gönder",
gift_type: "Hediye Türü",
prop_type_ride: "Araç",
prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Avatar Çerçevesi",
prop_type_badge: "Rozet",
prop_type_chat_bubble: "Sohbet Balonu",
prop_type_data_card: "Profil Kartı",
no_giftable_props: "Gönderilebilir eşya yok",
send: "Gönder",
sending: "Gönderiliyor...",
send_success: "Başarıyla gönderildi",
ban_user: "Kullanıcıyı Yasakla",
ban_reason: "Yasaklama nedeni",
ban: "Yasakla",
submitting: "Gönderiliyor...",
ban_success: "Kullanıcı yasaklandı",
unban_user: "Yasağı Kaldır",
unban_reason: "Yasağı kaldırma nedeni",
unban: "Yasağı kaldır",
unban_success: "Kullanıcının yasağı kaldırıldı",
can_unban: "Yasak kaldırılabilir",
cannot_unban: "Yasak kaldırılamaz",
loading: "Yükleniyor...",
uid_label: "UID",
days: "{count} gün",
recharged: "Yükleme yaptı",
not_recharged: "Yükleme yapmadı",
recharge_unknown: "Yükleme durumu bilinmiyor",
can_ban: "Yasaklanabilir",
cannot_ban: "Yasaklanamaz",
missing_token: "Token eksik",
not_manager: "Mevcut hesap yönetici değil",
user_not_found: "Kullanıcı bulunamadı",
user_recharged: "Bu kullanıcının yükleme kaydı var, yasaklanamaz",
user_already_banned: "Bu kullanıcı zaten yasaklı",
user_not_banned: "Bu kullanıcı yasaklı değil",
prop_not_giftable: "Bu eşya yönetici hediyesi için açık değil",
vip_not_giftable: "Bu noble eşyası gönderilemez",
request_failed: "İstek başarısız",
enter_user: "Kullanıcı ID veya hesap gir"
},
id: {
document_title: "Pusat Manajer",
page_label: "Pusat Manajer",
back: "Kembali",
title: "Pusat Manajer",
language_button_aria: "Ubah bahasa",
close: "Tutup",
current_manager: "Manajer Saat Ini",
user: "Pengguna",
user_search_placeholder: "Masukkan ID pengguna atau akun",
search: "Cari",
searching: "Mencari...",
target_user: "Pengguna Target",
gift_props: "Kirim Properti",
gift_type: "Jenis Hadiah",
prop_type_ride: "Kendaraan",
prop_type_noble_vip: "Noble VIP",
prop_type_avatar_frame: "Bingkai Avatar",
prop_type_badge: "Lencana",
prop_type_chat_bubble: "Gelembung Chat",
prop_type_data_card: "Kartu Profil",
no_giftable_props: "Tidak ada properti yang dapat dikirim",
send: "Kirim",
sending: "Mengirim...",
send_success: "Berhasil dikirim",
ban_user: "Blokir Pengguna",
ban_reason: "Alasan blokir",
ban: "Blokir",
submitting: "Mengirim...",
ban_success: "Pengguna diblokir",
unban_user: "Buka Blokir Pengguna",
unban_reason: "Alasan buka blokir",
unban: "Buka blokir",
unban_success: "Blokir pengguna dibuka",
can_unban: "Dapat dibuka",
cannot_unban: "Tidak dapat dibuka",
loading: "Memuat...",
uid_label: "UID",
days: "{count} hari",
recharged: "Sudah isi ulang",
not_recharged: "Belum isi ulang",
recharge_unknown: "Status isi ulang tidak diketahui",
can_ban: "Dapat diblokir",
cannot_ban: "Tidak dapat diblokir",
missing_token: "Token tidak ditemukan",
not_manager: "Akun saat ini bukan manajer",
user_not_found: "Pengguna tidak ditemukan",
user_recharged: "Pengguna ini memiliki riwayat isi ulang dan tidak dapat diblokir",
user_already_banned: "Pengguna ini sudah diblokir",
user_not_banned: "Pengguna ini tidak diblokir",
prop_not_giftable: "Properti ini tidak tersedia untuk hadiah manajer",
vip_not_giftable: "Properti noble ini tidak dapat dikirim",
request_failed: "Permintaan gagal",
enter_user: "Masukkan ID pengguna atau akun"
}
};
const state = {
manager: null,
target: null,
propsType: defaultPropsType,
props: [],
selectedPropsId: ""
};
const params = readURLParams();
let currentLang = normalizeLanguage(params.get("lang"));
let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
function readURLParams() {
const result = new URLSearchParams(window.location.search);
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
const hashParams = new URLSearchParams(hashQuery);
hashParams.forEach((value, key) => {
if (!result.has(key)) result.set(key, value);
});
return result;
}
function readRawParam(name) {
const queries = [window.location.search.replace(/^\?/, "")];
if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]);
for (const query of queries) {
for (const part of query.split("&")) {
if (!part) continue;
const equalIndex = part.indexOf("=");
const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part;
const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : "";
let decodedKey = "";
try {
decodedKey = decodeURIComponent(rawKey);
} catch (error) {
continue;
}
if (decodedKey !== name) continue;
try {
return decodeURIComponent(rawValue.replace(/\+/g, "%2B"));
} catch (error) {
return rawValue;
}
}
}
return "";
}
function normalizeLanguage(lang) {
const value = String(lang || "en").toLowerCase();
return supportedLanguages.includes(value) ? value : "en";
}
async function loadMessages(lang) {
const normalizedLang = normalizeLanguage(lang);
if (window.location.protocol === "file:") return fallbackMessages[normalizedLang];
try {
const response = await fetch(`./locales/${normalizedLang}.json?v=20260511-0107`, { cache: "no-store" });
if (response.ok) return await response.json();
} catch (error) {
return fallbackMessages[normalizedLang];
}
return fallbackMessages[normalizedLang];
}
function localizedValue(messages, key) {
return messages?.[key] || fallbackMessages[currentLang]?.[key] || fallbackMessages.en[key] || "";
}
function message(key, fallback = "") {
return localizedValue(currentMessages, key) || fallback || key;
}
function renderMessages(messages) {
document.querySelectorAll("[data-i18n]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18n);
if (value) node.textContent = value;
});
document.querySelectorAll("[data-i18n-aria]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18nAria);
if (value) node.setAttribute("aria-label", value);
});
document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => {
const value = localizedValue(messages, node.dataset.i18nPlaceholder);
if (value) node.setAttribute("placeholder", value);
});
}
function setLanguageMenuOpen(open) {
const languageButton = $(".language-button");
const languageMenu = $(".language-menu");
if (!languageButton || !languageMenu) return;
languageMenu.hidden = !open;
languageButton.setAttribute("aria-expanded", String(open));
}
function renderLanguageControls(messages) {
const languageButton = $(".language-button");
if (languageButton) {
languageButton.textContent = languageLabels[currentLang];
languageButton.setAttribute("aria-label", localizedValue(messages, "language_button_aria") || "Change language");
}
document.querySelectorAll(".language-menu [data-lang]").forEach((button) => {
const isActive = button.dataset.lang === currentLang;
button.classList.toggle("is-active", isActive);
button.setAttribute("aria-pressed", String(isActive));
});
}
function setPropsTypeMenuOpen(open) {
const root = $("#propsTypeSelect");
const button = $("#propsTypeButton");
const menu = $("#propsTypeMenu");
if (!root || !button || !menu) return;
root.classList.toggle("is-open", open);
menu.hidden = !open;
button.setAttribute("aria-expanded", String(open));
}
function renderPropsTypeSelect() {
const root = $("#propsTypeSelect");
const label = $("#propsTypeSelectedLabel");
if (root) root.dataset.value = state.propsType;
if (label) label.textContent = propTypeLabel(state.propsType);
document.querySelectorAll("#propsTypeMenu [data-value]").forEach((button) => {
const isSelected = button.dataset.value === state.propsType;
button.classList.toggle("is-selected", isSelected);
button.setAttribute("aria-selected", String(isSelected));
});
}
async function choosePropsType(value) {
const nextValue = String(value || defaultPropsType);
setPropsTypeMenuOpen(false);
if (nextValue === state.propsType) {
renderPropsTypeSelect();
return;
}
state.propsType = nextValue;
renderPropsTypeSelect();
try {
await loadProps();
} catch (error) {
setStatus($("#propsStatus"), friendlyError(error), "error");
}
}
async function applyLanguage(lang) {
currentLang = normalizeLanguage(lang);
const messages = await loadMessages(currentLang);
currentMessages = messages;
document.documentElement.lang = currentLang;
document.documentElement.dir = currentLang === "ar" ? "rtl" : "ltr";
document.title = message("document_title", "Manager Center");
renderMessages(messages);
renderLanguageControls(messages);
renderPropsTypeSelect();
renderManager(state.manager);
renderTarget();
renderProps();
}
function $(selector) {
return document.querySelector(selector);
}
function apiBaseURL() {
const override = window.HYAPP_API_BASE_URL || params.get("apiBase");
if (override) return String(override).replace(/\/+$/, "");
return API_BASE_URL.replace(/\/+$/, "");
}
function isLocalDocumentsFile() {
if (window.location.protocol !== "file:") return false;
let pathname = window.location.pathname || "";
try {
pathname = decodeURIComponent(pathname);
} catch (error) {
return false;
}
return pathname.startsWith("/Users/hy/Documents/");
}
function goApiBaseURL() {
const override = window.HYAPP_GO_API_BASE_URL || params.get("goApiBase") || params.get("goBase");
if (override) return String(override).replace(/\/+$/, "");
if (isLocalDocumentsFile()) return `${LOCAL_GO_API_BASE_URL}${GO_API_PATH_PREFIX}`;
return `${apiBaseURL()}${GO_API_PATH_PREFIX}`;
}
function buildRequestURL(baseURL, path) {
const base = `${String(baseURL || API_BASE_URL).replace(/\/+$/, "")}/`;
const target = String(path || "").replace(/^\/+/, "");
return new URL(target, base).toString();
}
function normalizeAuthorization(token) {
const value = String(token || "").trim();
if (!value) return "";
return /^bearer\s+/i.test(value) ? value : `Bearer ${value}`;
}
function authHeadersFromToken(token) {
const sysOrigin = params.get("sysOrigin") || params.get("origin") || "ATYOU";
return {
Authorization: normalizeAuthorization(token),
"req-lang": currentLang,
"req-client": "H5",
"req-version": "V2",
"req-zone": Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai",
"req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=Web;channel=Web",
"req-sys-origin": `origin=${sysOrigin};originChild=${sysOrigin}`
};
}
async function requestGoJSON(path, options = {}) {
const token = readRawParam("token");
if (!token) {
const error = new Error(message("missing_token"));
error.code = "missing_token";
throw error;
}
const headers = authHeadersFromToken(token);
const request = {
method: options.method || "GET",
cache: "no-store",
headers
};
if (options.body !== undefined) {
headers["Content-Type"] = "application/json";
request.body = JSON.stringify(options.body);
}
const response = await fetch(buildRequestURL(goApiBaseURL(), path), request);
const data = await response.json().catch(() => ({}));
if (!response.ok || data.status === false) {
const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
error.code = data.code || data.errorCodeName || "";
error.response = data;
throw error;
}
return data.body ?? data.data ?? null;
}
function endpointProfile() {
return "/app/h5/manager-center/profile";
}
function endpointSearchUser(account) {
return `/app/h5/manager-center/users/search?account=${encodeURIComponent(account)}`;
}
function endpointProps(type) {
return `/app/h5/manager-center/props?type=${encodeURIComponent(type || "")}`;
}
function endpointSendProps() {
return "/app/h5/manager-center/props/send";
}
function endpointBanUser() {
return "/app/h5/manager-center/users/ban";
}
function endpointUnbanUser() {
return "/app/h5/manager-center/users/unban";
}
function setLoading(isLoading) {
const root = $(".manager-center");
const mask = $("#loadingMask");
if (root) root.dataset.loading = isLoading ? "true" : "false";
if (mask) mask.hidden = !isLoading;
}
function setStatus(node, text, type = "") {
if (!node) return;
node.textContent = text || "";
node.hidden = !text;
node.classList.toggle("is-error", type === "error");
}
function showToast(text) {
const toast = $("#toast");
if (!toast) return;
toast.textContent = text || "";
toast.hidden = !text;
window.clearTimeout(showToast.timer);
showToast.timer = window.setTimeout(() => {
toast.hidden = true;
}, 2300);
}
function syncModalState() {
document.body.classList.toggle("is-modal-open", Boolean(document.querySelector(".modal-mask:not([hidden])")));
}
function setModalOpen(selector, open) {
const modal = $(selector);
if (!modal) return;
modal.hidden = !open;
if (open) {
setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false);
}
syncModalState();
}
function closeModals() {
document.querySelectorAll(".modal-mask").forEach((modal) => {
modal.hidden = true;
});
setPropsTypeMenuOpen(false);
syncModalState();
}
function openGiftModal() {
if (!state.target) {
showToast(message("enter_user"));
return;
}
setModalOpen("#giftModal", true);
updateActionState();
}
function openBanModal() {
if (!state.target) {
showToast(message("enter_user"));
return;
}
setStatus($("#banStatus"), "");
setModalOpen("#banModal", true);
updateActionState();
}
function openUnbanModal() {
if (!state.target) {
showToast(message("enter_user"));
return;
}
setStatus($("#unbanStatus"), "");
setModalOpen("#unbanModal", true);
updateActionState();
}
function closePage() {
try {
if (window.app && typeof window.app.closePage === "function") {
window.app.closePage();
return;
}
if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") {
window.FlutterPageControl.postMessage("close_page");
return;
}
if (window.history.length > 1) window.history.back();
} catch (error) {
console.error("close manager center failed:", error);
if (window.history.length > 1) window.history.back();
}
}
function firstLetter(name, fallback) {
const value = String(name || fallback || "U").trim();
return value ? value.slice(0, 1).toUpperCase() : "U";
}
function renderAvatar(imgSelector, fallbackSelector, url, name, fallback) {
const img = $(imgSelector);
const fallbackNode = $(fallbackSelector);
if (!img || !fallbackNode) return;
const src = String(url || "").trim();
fallbackNode.textContent = firstLetter(name, fallback);
if (src) {
img.src = src;
img.hidden = false;
fallbackNode.hidden = true;
} else {
img.removeAttribute("src");
img.hidden = true;
fallbackNode.hidden = false;
}
}
function displayName(user) {
return user?.userNickname || user?.nickname || user?.name || "-";
}
function displayUserID(user) {
return String(user?.userId || user?.id || "-");
}
function displayShortID(user) {
return String(user?.actualAccount || user?.account || user?.shortId || user?.userId || user?.id || "-");
}
function canBanTarget(target) {
return Boolean(target) && target.canBan !== false && !target.hasRecharge && !canUnbanTarget(target);
}
function canUnbanTarget(target) {
const status = String(target?.accountStatus || "").trim().toUpperCase();
return Boolean(target) && (status === "FREEZE" || status === "ARCHIVE" || status === "ARCHIVE_DEVICE");
}
function userIDText(user) {
return `${message("uid_label", "UID")}: ${displayShortID(user)}`;
}
function propTypeLabel(type) {
const key = `prop_type_${String(type || "").toLowerCase()}`;
return message(key, type || "-");
}
function formatDays(days) {
return message("days", "{count} days").replace("{count}", String(days || 0));
}
function giftDays(item) {
const days = Number(item?.days);
if (Number.isFinite(days) && days > 0) return days;
return String(item?.type || state.propsType).toUpperCase() === "NOBLE_VIP" ? 3 : 7;
}
function renderManager(manager) {
$("#managerName").textContent = displayName(manager);
$("#managerUid").textContent = userIDText(manager);
renderAvatar("#managerAvatar", "#managerAvatarFallback", manager?.userAvatar, displayName(manager), "M");
}
function renderTarget() {
const card = $("#targetCard");
const actionCard = $("#actionCard");
if (!state.target) {
if (card) card.hidden = true;
if (actionCard) actionCard.hidden = true;
closeModals();
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 || "-";
const canBan = canBanTarget(target);
const banEligibility = $("#banEligibility");
if (banEligibility) {
banEligibility.textContent = canBan ? message("can_ban") : message("cannot_ban");
banEligibility.classList.toggle("is-danger", !canBan);
}
const canUnban = canUnbanTarget(target);
const unbanEligibility = $("#unbanEligibility");
if (unbanEligibility) {
unbanEligibility.textContent = canUnban ? message("can_unban") : message("cannot_unban");
unbanEligibility.classList.toggle("is-danger", !canUnban);
}
renderAvatar("#targetAvatar", "#targetAvatarFallback", target.userAvatar, displayName(target), "U");
updateActionState();
}
function renderProps() {
const list = $("#propsList");
if (!list) return;
list.replaceChildren();
if (!state.props.length) {
setStatus($("#propsStatus"), message("no_giftable_props"));
updateActionState();
return;
}
setStatus($("#propsStatus"), "");
state.props.forEach((item) => {
const button = document.createElement("button");
button.type = "button";
button.className = "prop-option";
button.dataset.propsId = String(item.id);
if (String(item.id) === String(state.selectedPropsId)) {
button.classList.add("is-selected");
}
const cover = document.createElement("span");
cover.className = "prop-cover";
if (item.cover || item.sourceUrl) {
const img = document.createElement("img");
img.alt = "";
img.src = item.cover || item.sourceUrl;
cover.appendChild(img);
} else {
const fallback = document.createElement("span");
fallback.textContent = firstLetter(item.name, "G");
cover.appendChild(fallback);
}
const copy = document.createElement("span");
copy.className = "prop-copy";
const name = document.createElement("span");
name.className = "prop-name";
name.textContent = item.name || `ID ${item.id}`;
const meta = document.createElement("span");
meta.className = "prop-meta";
meta.textContent = item.vipLevel ? `${propTypeLabel(item.type)} · VIP ${item.vipLevel}` : propTypeLabel(item.type);
copy.append(name, meta);
const days = document.createElement("span");
days.className = "prop-days";
days.textContent = formatDays(giftDays(item));
button.append(cover, copy, days);
button.addEventListener("click", () => {
state.selectedPropsId = String(item.id);
renderProps();
updateActionState();
});
list.appendChild(button);
});
updateActionState();
}
function updateActionState() {
const giftEntryButton = $("#giftEntryButton");
const banEntryButton = $("#banEntryButton");
const unbanEntryButton = $("#unbanEntryButton");
const sendButton = $("#sendPropsButton");
const banButton = $("#banButton");
const unbanButton = $("#unbanButton");
if (giftEntryButton) giftEntryButton.disabled = !state.target;
if (banEntryButton) banEntryButton.disabled = !state.target;
if (unbanEntryButton) unbanEntryButton.disabled = !state.target;
if (sendButton) sendButton.disabled = !state.target || !state.selectedPropsId;
if (banButton) banButton.disabled = !canBanTarget(state.target);
if (unbanButton) unbanButton.disabled = !canUnbanTarget(state.target);
}
async function loadProfile() {
const body = await requestGoJSON(endpointProfile());
state.manager = body?.manager || body || {};
renderManager(state.manager);
}
async function loadProps() {
state.props = [];
state.selectedPropsId = "";
renderProps();
setStatus($("#propsStatus"), message("loading"));
const body = await requestGoJSON(endpointProps(state.propsType));
state.props = Array.isArray(body?.records) ? body.records : [];
state.selectedPropsId = state.props.length ? String(state.props[0].id) : "";
renderProps();
}
async function searchUser(event) {
event.preventDefault();
const input = $("#accountInput");
const account = String(input?.value || "").trim();
if (!account) {
setStatus($("#searchStatus"), message("enter_user"), "error");
return;
}
$("#searchButton").disabled = true;
setStatus($("#searchStatus"), message("searching"));
try {
const body = await requestGoJSON(endpointSearchUser(account));
state.target = body?.user || body || null;
renderTarget();
setStatus($("#searchStatus"), "");
} catch (error) {
state.target = null;
renderTarget();
setStatus($("#searchStatus"), friendlyError(error), "error");
} finally {
$("#searchButton").disabled = false;
}
}
async function sendProps() {
if (!state.target || !state.selectedPropsId) return;
const button = $("#sendPropsButton");
button.disabled = true;
try {
await requestGoJSON(endpointSendProps(), {
method: "POST",
body: {
acceptUserId: displayUserID(state.target),
propsId: state.selectedPropsId
}
});
setModalOpen("#giftModal", false);
showToast(message("send_success"));
} catch (error) {
showToast(friendlyError(error));
} finally {
updateActionState();
}
}
async function banUser() {
if (!canBanTarget(state.target)) return;
const button = $("#banButton");
button.disabled = true;
setStatus($("#banStatus"), message("submitting"));
try {
await requestGoJSON(endpointBanUser(), {
method: "POST",
body: {
userId: displayUserID(state.target),
reason: $("#banReason").value || ""
}
});
state.target.accountStatus = "ARCHIVE";
renderTarget();
setModalOpen("#banModal", false);
setStatus($("#banStatus"), "");
showToast(message("ban_success"));
} catch (error) {
setStatus($("#banStatus"), friendlyError(error), "error");
} finally {
updateActionState();
}
}
async function unbanUser() {
if (!canUnbanTarget(state.target)) return;
const button = $("#unbanButton");
button.disabled = true;
setStatus($("#unbanStatus"), message("submitting"));
try {
await requestGoJSON(endpointUnbanUser(), {
method: "POST",
body: {
userId: displayUserID(state.target),
reason: $("#unbanReason").value || ""
}
});
state.target.accountStatus = "NORMAL";
renderTarget();
setModalOpen("#unbanModal", false);
setStatus($("#unbanStatus"), "");
showToast(message("unban_success"));
} catch (error) {
setStatus($("#unbanStatus"), friendlyError(error), "error");
} finally {
updateActionState();
}
}
function friendlyError(error) {
const code = error?.code || error?.response?.code || error?.response?.errorCodeName || "";
if (code === "missing_token") return message("missing_token");
if (code === "not_manager") return message("not_manager");
if (code === "user_not_found") return message("user_not_found");
if (code === "user_recharged") return message("user_recharged");
if (code === "user_already_banned") return message("user_already_banned");
if (code === "user_not_banned") return message("user_not_banned");
if (code === "prop_not_giftable") return message("prop_not_giftable");
if (code === "vip_not_giftable") return message("vip_not_giftable");
return error?.message || message("request_failed");
}
function bindEvents() {
$(".back-button")?.addEventListener("click", closePage);
$(".language-button")?.addEventListener("click", (event) => {
event.stopPropagation();
setLanguageMenuOpen(Boolean($(".language-menu")?.hidden));
});
document.querySelectorAll(".language-menu [data-lang]").forEach((button) => {
button.addEventListener("click", async (event) => {
event.stopPropagation();
setLanguageMenuOpen(false);
await applyLanguage(button.dataset.lang);
});
});
document.addEventListener("click", () => {
setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false);
});
$("#giftEntryButton")?.addEventListener("click", openGiftModal);
$("#banEntryButton")?.addEventListener("click", openBanModal);
$("#unbanEntryButton")?.addEventListener("click", openUnbanModal);
document.querySelectorAll("[data-modal-close]").forEach((button) => {
button.addEventListener("click", closeModals);
});
document.querySelectorAll(".modal-mask").forEach((modal) => {
modal.addEventListener("click", (event) => {
if (event.target === modal) closeModals();
});
});
$("#propsTypeButton")?.addEventListener("click", (event) => {
event.stopPropagation();
setLanguageMenuOpen(false);
setPropsTypeMenuOpen(Boolean($("#propsTypeMenu")?.hidden));
});
document.querySelectorAll("#propsTypeMenu [data-value]").forEach((button) => {
button.addEventListener("click", async (event) => {
event.stopPropagation();
await choosePropsType(button.dataset.value);
});
});
document.addEventListener("keydown", (event) => {
if (event.key !== "Escape") return;
setLanguageMenuOpen(false);
setPropsTypeMenuOpen(false);
closeModals();
});
$("#searchForm")?.addEventListener("submit", searchUser);
$("#sendPropsButton")?.addEventListener("click", sendProps);
$("#banButton")?.addEventListener("click", banUser);
$("#unbanButton")?.addEventListener("click", unbanUser);
}
async function init() {
bindEvents();
renderPropsTypeSelect();
await applyLanguage(currentLang);
if (!readRawParam("token")) {
setLoading(false);
setStatus($("#searchStatus"), message("missing_token"), "error");
return;
}
try {
await Promise.all([loadProfile(), loadProps()]);
} catch (error) {
showToast(friendlyError(error));
} finally {
setLoading(false);
}
}
init();
})();