2026-05-11 18:45:50 +08:00

829 lines
28 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",
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_chat_bubble: "Chat Bubble",
prop_type_data_card: "Profile Card",
no_giftable_props: "No giftable props",
send: "Send",
sending: "Sending...",
confirm_send: "Send this prop?",
send_success: "Sent successfully",
ban_user: "Ban User",
ban_reason: "Ban reason",
ban: "Ban",
confirm_ban: "Ban this user?",
submitting: "Submitting...",
ban_success: "User banned",
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",
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: "تغيير اللغة",
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_chat_bubble: "فقاعة الدردشة",
prop_type_data_card: "بطاقة الملف",
no_giftable_props: "لا توجد عناصر قابلة للإهداء",
send: "إرسال",
sending: "جار الإرسال...",
confirm_send: "هل تريد إرسال هذا العنصر؟",
send_success: "تم الإرسال بنجاح",
ban_user: "حظر المستخدم",
ban_reason: "سبب الحظر",
ban: "حظر",
confirm_ban: "هل تريد حظر هذا المستخدم؟",
submitting: "جار الإرسال...",
ban_success: "تم حظر المستخدم",
loading: "جار التحميل...",
uid_label: "UID",
days: "{count} يوم",
recharged: "تم الشحن",
not_recharged: "لم يتم الشحن",
recharge_unknown: "حالة الشحن غير معروفة",
can_ban: "يمكن الحظر",
cannot_ban: "لا يمكن الحظر",
missing_token: "رمز الدخول مفقود",
not_manager: "الحساب الحالي ليس مديرًا",
user_not_found: "المستخدم غير موجود",
user_recharged: "لدى هذا المستخدم سجلات شحن ولا يمكن حظره",
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",
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_chat_bubble: "Sohbet Balonu",
prop_type_data_card: "Profil Kartı",
no_giftable_props: "Gönderilebilir eşya yok",
send: "Gönder",
sending: "Gönderiliyor...",
confirm_send: "Bu eşyayı göndermek istiyor musun?",
send_success: "Başarıyla gönderildi",
ban_user: "Kullanıcıyı Yasakla",
ban_reason: "Yasaklama nedeni",
ban: "Yasakla",
confirm_ban: "Bu kullanıcıyı yasaklamak istiyor musun?",
submitting: "Gönderiliyor...",
ban_success: "Kullanıcı yasaklandı",
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",
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",
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_chat_bubble: "Gelembung Chat",
prop_type_data_card: "Kartu Profil",
no_giftable_props: "Tidak ada properti yang dapat dikirim",
send: "Kirim",
sending: "Mengirim...",
confirm_send: "Kirim properti ini?",
send_success: "Berhasil dikirim",
ban_user: "Blokir Pengguna",
ban_reason: "Alasan blokir",
ban: "Blokir",
confirm_ban: "Blokir pengguna ini?",
submitting: "Mengirim...",
ban_success: "Pengguna diblokir",
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",
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-0105`, { 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 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 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;
}
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 renderManager(manager) {
$("#managerName").textContent = displayName(manager);
$("#managerUid").textContent = userIDText(manager);
renderAvatar("#managerAvatar", "#managerAvatarFallback", manager?.userAvatar, displayName(manager), "M");
}
function renderTarget() {
const card = $("#targetCard");
const banCard = $("#banCard");
if (!state.target) {
if (card) card.hidden = true;
if (banCard) banCard.hidden = true;
updateActionState();
return;
}
const target = state.target;
card.hidden = false;
banCard.hidden = false;
$("#targetName").textContent = displayName(target);
$("#targetUid").textContent = userIDText(target);
$("#targetAccountStatus").textContent = target.accountStatus || "-";
const canBan = canBanTarget(target);
const banEligibility = $("#banEligibility");
banEligibility.textContent = canBan ? message("can_ban") : message("cannot_ban");
banEligibility.classList.toggle("is-danger", !canBan);
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(item.days);
button.append(cover, copy, days);
button.addEventListener("click", () => {
state.selectedPropsId = String(item.id);
renderProps();
updateActionState();
});
list.appendChild(button);
});
updateActionState();
}
function updateActionState() {
const sendButton = $("#sendPropsButton");
const banButton = $("#banButton");
if (sendButton) sendButton.disabled = !state.target || !state.selectedPropsId;
if (banButton) banButton.disabled = !canBanTarget(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;
if (!window.confirm(message("confirm_send"))) return;
const button = $("#sendPropsButton");
button.disabled = true;
try {
await requestGoJSON(endpointSendProps(), {
method: "POST",
body: {
acceptUserId: displayUserID(state.target),
propsId: state.selectedPropsId
}
});
showToast(message("send_success"));
} catch (error) {
showToast(friendlyError(error));
} finally {
updateActionState();
}
}
async function banUser() {
if (!canBanTarget(state.target)) return;
if (!window.confirm(message("confirm_ban"))) 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();
setStatus($("#banStatus"), "");
showToast(message("ban_success"));
} catch (error) {
setStatus($("#banStatus"), 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 === "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);
});
$("#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);
});
$("#searchForm")?.addEventListener("submit", searchUser);
$("#sendPropsButton")?.addEventListener("click", sendProps);
$("#banButton")?.addEventListener("click", banUser);
}
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();
})();