1672 lines
59 KiB
JavaScript
1672 lines
59 KiB
JavaScript
(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",
|
||
current_country: "Current country",
|
||
change_country: "Change Country",
|
||
change_country_disabled: "Change Country disabled",
|
||
select_country: "Select country",
|
||
changing_country: "Changing...",
|
||
country_changed: "Country changed",
|
||
no_countries: "No countries available",
|
||
bd_leaders: "BD Leaders",
|
||
bd_leader_count: "{count} BD Leaders",
|
||
refresh: "Refresh",
|
||
refreshing: "Refreshing...",
|
||
no_bd_leaders: "No BD Leaders yet",
|
||
add_bd_leader: "Add BD Leader",
|
||
add_bd_leader_disabled: "Add BD Leader disabled",
|
||
adding_bd_leader: "Adding...",
|
||
add_bd_leader_success: "BD Leader added",
|
||
gift_props: "Gift Props",
|
||
gift_props_disabled: "Gift Props disabled",
|
||
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_user_disabled: "Ban User disabled",
|
||
ban_reason: "Ban reason",
|
||
ban: "Ban",
|
||
submitting: "Submitting...",
|
||
ban_success: "User banned",
|
||
unban_user: "Unban User",
|
||
unban_user_disabled: "Unban User disabled",
|
||
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",
|
||
manager_region_required: "Manager region is required",
|
||
bd_leader_origin_mismatch: "The user is not in this manager's origin",
|
||
user_is_admin: "Admin users cannot be added as BD Leaders",
|
||
user_already_bd_leader: "This user is already a BD Leader",
|
||
user_already_bound_bd_leader: "This user is already bound to a BD Leader",
|
||
user_identity_locked: "This user has Host, Agency, BD, BD Leader, or Coin Seller identity and cannot be moved",
|
||
country_not_found: "Country not found",
|
||
country_required: "Select country",
|
||
prop_not_giftable: "This prop is not available for manager gifting",
|
||
vip_not_giftable: "This noble prop cannot be gifted",
|
||
manager_feature_disabled: "This feature is disabled by admin",
|
||
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: "المستخدم المستهدف",
|
||
current_country: "Current country",
|
||
change_country: "Change Country",
|
||
change_country_disabled: "Change Country disabled",
|
||
select_country: "Select country",
|
||
changing_country: "Changing...",
|
||
country_changed: "Country changed",
|
||
no_countries: "No countries available",
|
||
bd_leaders: "قادة BD",
|
||
bd_leader_count: "{count} قادة BD",
|
||
refresh: "تحديث",
|
||
refreshing: "جار التحديث...",
|
||
no_bd_leaders: "لا يوجد قادة BD بعد",
|
||
add_bd_leader: "إضافة قائد BD",
|
||
add_bd_leader_disabled: "إضافة قائد BD معطلة",
|
||
adding_bd_leader: "جار الإضافة...",
|
||
add_bd_leader_success: "تمت إضافة قائد BD",
|
||
gift_props: "إرسال العناصر",
|
||
gift_props_disabled: "إرسال العناصر معطل",
|
||
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_user_disabled: "حظر المستخدم معطل",
|
||
ban_reason: "سبب الحظر",
|
||
ban: "حظر",
|
||
submitting: "جار الإرسال...",
|
||
ban_success: "تم حظر المستخدم",
|
||
unban_user: "إلغاء حظر المستخدم",
|
||
unban_user_disabled: "إلغاء حظر المستخدم معطل",
|
||
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: "هذا المستخدم غير محظور",
|
||
manager_region_required: "منطقة المدير مطلوبة",
|
||
bd_leader_origin_mismatch: "المستخدم ليس ضمن نظام هذا المدير",
|
||
user_is_admin: "لا يمكن إضافة مستخدم إداري كقائد BD",
|
||
user_already_bd_leader: "هذا المستخدم قائد BD بالفعل",
|
||
user_already_bound_bd_leader: "هذا المستخدم مرتبط بالفعل بقائد BD",
|
||
user_identity_locked: "This user has Host, Agency, BD, BD Leader, or Coin Seller identity and cannot be moved",
|
||
country_not_found: "Country not found",
|
||
country_required: "Select country",
|
||
prop_not_giftable: "هذا العنصر غير متاح كهديّة من المدير",
|
||
vip_not_giftable: "لا يمكن إهداء عنصر VIP هذا",
|
||
manager_feature_disabled: "تم تعطيل هذه الميزة من الإدارة",
|
||
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ı",
|
||
current_country: "Current country",
|
||
change_country: "Change Country",
|
||
change_country_disabled: "Change Country disabled",
|
||
select_country: "Select country",
|
||
changing_country: "Changing...",
|
||
country_changed: "Country changed",
|
||
no_countries: "No countries available",
|
||
bd_leaders: "BD Liderleri",
|
||
bd_leader_count: "{count} BD Lideri",
|
||
refresh: "Yenile",
|
||
refreshing: "Yenileniyor...",
|
||
no_bd_leaders: "Henüz BD lideri yok",
|
||
add_bd_leader: "BD Lideri Ekle",
|
||
add_bd_leader_disabled: "BD lideri ekleme kapalı",
|
||
adding_bd_leader: "Ekleniyor...",
|
||
add_bd_leader_success: "BD lideri eklendi",
|
||
gift_props: "Eşya Gönder",
|
||
gift_props_disabled: "Eşya gönderme kapalı",
|
||
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_user_disabled: "Kullanıcı yasaklama kapalı",
|
||
ban_reason: "Yasaklama nedeni",
|
||
ban: "Yasakla",
|
||
submitting: "Gönderiliyor...",
|
||
ban_success: "Kullanıcı yasaklandı",
|
||
unban_user: "Yasağı Kaldır",
|
||
unban_user_disabled: "Yasak kaldırma kapalı",
|
||
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",
|
||
manager_region_required: "Yönetici bölgesi gerekli",
|
||
bd_leader_origin_mismatch: "Kullanıcı bu yöneticinin origin değerinde değil",
|
||
user_is_admin: "Admin kullanıcılar BD lideri eklenemez",
|
||
user_already_bd_leader: "Bu kullanıcı zaten BD lideri",
|
||
user_already_bound_bd_leader: "Bu kullanıcı zaten bir BD liderine bağlı",
|
||
user_identity_locked: "This user has Host, Agency, BD, BD Leader, or Coin Seller identity and cannot be moved",
|
||
country_not_found: "Country not found",
|
||
country_required: "Select country",
|
||
prop_not_giftable: "Bu eşya yönetici hediyesi için açık değil",
|
||
vip_not_giftable: "Bu noble eşyası gönderilemez",
|
||
manager_feature_disabled: "Bu özellik admin tarafından kapatıldı",
|
||
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",
|
||
current_country: "Current country",
|
||
change_country: "Change Country",
|
||
change_country_disabled: "Change Country disabled",
|
||
select_country: "Select country",
|
||
changing_country: "Changing...",
|
||
country_changed: "Country changed",
|
||
no_countries: "No countries available",
|
||
bd_leaders: "BD Leader",
|
||
bd_leader_count: "{count} BD Leader",
|
||
refresh: "Segarkan",
|
||
refreshing: "Menyegarkan...",
|
||
no_bd_leaders: "Belum ada BD Leader",
|
||
add_bd_leader: "Tambah BD Leader",
|
||
add_bd_leader_disabled: "Tambah BD Leader dinonaktifkan",
|
||
adding_bd_leader: "Menambahkan...",
|
||
add_bd_leader_success: "BD Leader ditambahkan",
|
||
gift_props: "Kirim Properti",
|
||
gift_props_disabled: "Kirim properti dinonaktifkan",
|
||
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_user_disabled: "Blokir pengguna dinonaktifkan",
|
||
ban_reason: "Alasan blokir",
|
||
ban: "Blokir",
|
||
submitting: "Mengirim...",
|
||
ban_success: "Pengguna diblokir",
|
||
unban_user: "Buka Blokir Pengguna",
|
||
unban_user_disabled: "Buka blokir pengguna dinonaktifkan",
|
||
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",
|
||
manager_region_required: "Region manajer diperlukan",
|
||
bd_leader_origin_mismatch: "Pengguna tidak berada di origin manajer ini",
|
||
user_is_admin: "Pengguna admin tidak dapat ditambahkan sebagai BD Leader",
|
||
user_already_bd_leader: "Pengguna ini sudah menjadi BD Leader",
|
||
user_already_bound_bd_leader: "Pengguna ini sudah terhubung ke BD Leader",
|
||
user_identity_locked: "This user has Host, Agency, BD, BD Leader, or Coin Seller identity and cannot be moved",
|
||
country_not_found: "Country not found",
|
||
country_required: "Select country",
|
||
prop_not_giftable: "Properti ini tidak tersedia untuk hadiah manajer",
|
||
vip_not_giftable: "Properti noble ini tidak dapat dikirim",
|
||
manager_feature_disabled: "Fitur ini dinonaktifkan oleh admin",
|
||
request_failed: "Permintaan gagal",
|
||
enter_user: "Masukkan ID pengguna atau akun"
|
||
}
|
||
};
|
||
|
||
const state = {
|
||
manager: null,
|
||
target: null,
|
||
activeAction: "",
|
||
features: defaultFeatures(),
|
||
propsType: defaultPropsType,
|
||
props: [],
|
||
selectedPropsId: "",
|
||
countries: [],
|
||
selectedCountryId: "",
|
||
countriesLoading: false,
|
||
changingCountry: false,
|
||
bdLeaders: [],
|
||
bdLeadersLoading: false,
|
||
bdLeadersError: "",
|
||
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;
|
||
|
||
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=20260625-0100`, { 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 setCountryMenuOpen(open) {
|
||
const root = $("#countrySelect");
|
||
const button = $("#countryButton");
|
||
const menu = $("#countryMenu");
|
||
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));
|
||
});
|
||
}
|
||
|
||
function renderCountrySelect() {
|
||
const root = $("#countrySelect");
|
||
const label = $("#countrySelectedLabel");
|
||
const menu = $("#countryMenu");
|
||
if (root) root.dataset.value = state.selectedCountryId || "";
|
||
if (label) label.textContent = countryLabel(selectedCountry()) || message("select_country");
|
||
if (!menu) return;
|
||
|
||
menu.replaceChildren();
|
||
state.countries.forEach((country) => {
|
||
const button = document.createElement("button");
|
||
button.type = "button";
|
||
button.setAttribute("role", "option");
|
||
button.dataset.value = String(country.id);
|
||
button.textContent = countryLabel(country);
|
||
const isSelected = String(country.id) === String(state.selectedCountryId);
|
||
button.classList.toggle("is-selected", isSelected);
|
||
button.setAttribute("aria-selected", String(isSelected));
|
||
button.addEventListener("click", (event) => {
|
||
event.stopPropagation();
|
||
chooseCountry(country.id);
|
||
});
|
||
menu.appendChild(button);
|
||
});
|
||
}
|
||
|
||
function chooseCountry(countryId) {
|
||
state.selectedCountryId = String(countryId || "");
|
||
setCountryMenuOpen(false);
|
||
renderCountrySelect();
|
||
updateActionState();
|
||
}
|
||
|
||
async function choosePropsType(value) {
|
||
const nextValue = String(value || defaultPropsType);
|
||
setPropsTypeMenuOpen(false);
|
||
setCountryMenuOpen(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();
|
||
renderCountrySelect();
|
||
renderManager(state.manager);
|
||
renderTarget();
|
||
renderActionModal();
|
||
renderProps();
|
||
renderBDLeaders();
|
||
updateActionState();
|
||
}
|
||
|
||
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 endpointCountries() {
|
||
return "/app/h5/manager-center/countries";
|
||
}
|
||
|
||
function endpointChangeUserCountry() {
|
||
return "/app/h5/manager-center/users/country";
|
||
}
|
||
|
||
function endpointProps(type) {
|
||
return `/app/h5/manager-center/props?type=${encodeURIComponent(type || "")}`;
|
||
}
|
||
|
||
function endpointSendProps() {
|
||
return "/app/h5/manager-center/props/send";
|
||
}
|
||
|
||
function endpointBDLeaders() {
|
||
return "/app/h5/manager-center/bd-leaders";
|
||
}
|
||
|
||
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 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;
|
||
modal.hidden = !open;
|
||
if (open) {
|
||
setLanguageMenuOpen(false);
|
||
setPropsTypeMenuOpen(false);
|
||
setCountryMenuOpen(false);
|
||
}
|
||
syncModalState();
|
||
}
|
||
|
||
function closeModals() {
|
||
document.querySelectorAll(".modal-mask").forEach((modal) => {
|
||
modal.hidden = true;
|
||
});
|
||
state.activeAction = "";
|
||
state.target = null;
|
||
setPropsTypeMenuOpen(false);
|
||
setCountryMenuOpen(false);
|
||
renderTarget();
|
||
syncModalState();
|
||
}
|
||
|
||
function openActionModal(action) {
|
||
const config = actionConfigs[action];
|
||
if (!config) return;
|
||
if (config.feature && !featureEnabled(config.feature)) {
|
||
showToast(message(config.disabledKey));
|
||
return;
|
||
}
|
||
|
||
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);
|
||
}
|
||
|
||
function openGiftModal() {
|
||
openActionModal("giftProps");
|
||
}
|
||
|
||
function openCountryModal() {
|
||
openActionModal("changeCountry");
|
||
}
|
||
|
||
function openBanModal() {
|
||
openActionModal("banUser");
|
||
}
|
||
|
||
function openUnbanModal() {
|
||
openActionModal("unbanUser");
|
||
}
|
||
|
||
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 sameUserID(left, right) {
|
||
return displayUserID(left) === displayUserID(right);
|
||
}
|
||
|
||
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 defaultFeatures() {
|
||
return {
|
||
addBdLeader: true,
|
||
giftProps: true,
|
||
banUser: true,
|
||
unbanUser: true,
|
||
changeCountry: true
|
||
};
|
||
}
|
||
|
||
function normalizeFeatures(features) {
|
||
return {
|
||
addBdLeader: features?.addBdLeader !== false,
|
||
giftProps: features?.giftProps !== false,
|
||
banUser: features?.banUser !== false,
|
||
unbanUser: features?.unbanUser !== false,
|
||
changeCountry: features?.changeCountry !== false
|
||
};
|
||
}
|
||
|
||
function featureEnabled(key) {
|
||
return state.features?.[key] !== false;
|
||
}
|
||
|
||
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 displayCountryText(user) {
|
||
const name = String(user?.countryName || "").trim();
|
||
const code = String(user?.countryCode || "").trim();
|
||
if (name && code) return `${name} (${code})`;
|
||
return name || code || "-";
|
||
}
|
||
|
||
function countryLabel(country) {
|
||
if (!country) return "";
|
||
const name = String(country.countryName || "").trim();
|
||
const code = String(country.countryCode || "").trim();
|
||
if (name && code) return `${name} (${code})`;
|
||
return name || code || "";
|
||
}
|
||
|
||
function selectedCountry() {
|
||
return state.countries.find((country) => String(country.id) === String(state.selectedCountryId)) || null;
|
||
}
|
||
|
||
function matchingCountryId(user) {
|
||
const code = String(user?.countryCode || "").trim().toUpperCase();
|
||
if (!code) return "";
|
||
const country = state.countries.find((item) => String(item.countryCode || "").trim().toUpperCase() === code);
|
||
return country?.id || "";
|
||
}
|
||
|
||
function propTypeLabel(type) {
|
||
const key = `prop_type_${String(type || "").toLowerCase()}`;
|
||
return message(key, type || "-");
|
||
}
|
||
|
||
function formatDays(days) {
|
||
return message("days", "{count} days").replace("{count}", new Intl.NumberFormat("en-US").format(Number(days) || 0));
|
||
}
|
||
|
||
function formatBDLeaderCount(count) {
|
||
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);
|
||
if (Number.isNaN(date.getTime())) return "";
|
||
try {
|
||
return new Intl.DateTimeFormat(currentLang, {
|
||
month: "short",
|
||
day: "2-digit",
|
||
hour: "2-digit",
|
||
minute: "2-digit"
|
||
}).format(date);
|
||
} catch (error) {
|
||
return date.toLocaleString();
|
||
}
|
||
}
|
||
|
||
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");
|
||
if (!state.target) {
|
||
if (card) card.hidden = true;
|
||
renderActionModal();
|
||
updateActionState();
|
||
return;
|
||
}
|
||
|
||
const target = state.target;
|
||
if (card) card.hidden = false;
|
||
$("#targetName").textContent = displayName(target);
|
||
$("#targetUid").textContent = userIDText(target);
|
||
$("#targetAccountStatus").textContent = target.accountStatus || "-";
|
||
$("#targetCountryStatus").textContent = displayCountryText(target);
|
||
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");
|
||
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();
|
||
}
|
||
|
||
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 isTargetBDLeader() {
|
||
if (!state.target) return false;
|
||
return state.bdLeaders.some((leader) => sameUserID(leader, state.target));
|
||
}
|
||
|
||
function renderBDLeaders() {
|
||
const list = $("#bdLeaderList");
|
||
const count = $("#bdLeaderCount");
|
||
const summary = $("#bdLeaderSummary");
|
||
const leaderCountText = formatBDLeaderCount(state.bdLeaders.length);
|
||
if (count) count.textContent = leaderCountText;
|
||
if (summary) summary.textContent = formatBDLeaderSummary();
|
||
if (!list) return;
|
||
|
||
list.replaceChildren();
|
||
if (state.bdLeadersLoading) {
|
||
setStatus($("#bdLeaderStatus"), message("refreshing"));
|
||
updateActionState();
|
||
return;
|
||
}
|
||
if (state.bdLeadersError) {
|
||
setStatus($("#bdLeaderStatus"), state.bdLeadersError, "error");
|
||
updateActionState();
|
||
return;
|
||
}
|
||
if (!state.bdLeaders.length) {
|
||
setStatus($("#bdLeaderStatus"), message("no_bd_leaders"));
|
||
updateActionState();
|
||
return;
|
||
}
|
||
setStatus($("#bdLeaderStatus"), "");
|
||
|
||
state.bdLeaders.forEach((leader) => {
|
||
const row = document.createElement("div");
|
||
row.className = "bd-leader-row";
|
||
|
||
const avatar = document.createElement("span");
|
||
avatar.className = "bd-leader-avatar";
|
||
if (leader.userAvatar) {
|
||
const img = document.createElement("img");
|
||
img.alt = "";
|
||
img.src = leader.userAvatar;
|
||
avatar.appendChild(img);
|
||
} else {
|
||
const fallback = document.createElement("span");
|
||
fallback.textContent = firstLetter(displayName(leader), "B");
|
||
avatar.appendChild(fallback);
|
||
}
|
||
|
||
const copy = document.createElement("span");
|
||
copy.className = "bd-leader-copy";
|
||
const name = document.createElement("span");
|
||
name.className = "bd-leader-name";
|
||
name.textContent = displayName(leader);
|
||
const meta = document.createElement("span");
|
||
meta.className = "bd-leader-meta";
|
||
meta.textContent = userIDText(leader);
|
||
copy.append(name, meta);
|
||
|
||
const time = document.createElement("span");
|
||
time.className = "bd-leader-time";
|
||
time.textContent = formatDateTime(leader.createTime);
|
||
|
||
row.append(avatar, copy, time);
|
||
list.appendChild(row);
|
||
});
|
||
updateActionState();
|
||
}
|
||
|
||
function updateActionState() {
|
||
const addBDLeaderButton = $("#addBDLeaderButton");
|
||
const countryEntryButton = $("#countryEntryButton");
|
||
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.addingBDLeader;
|
||
addBDLeaderButton.classList.toggle("is-feature-disabled", disabledByFeature);
|
||
const title = addBDLeaderButton.querySelector(".action-item-title");
|
||
if (title) {
|
||
title.textContent = disabledByFeature
|
||
? message("add_bd_leader_disabled")
|
||
: state.addingBDLeader ? message("adding_bd_leader") : message("add_bd_leader");
|
||
}
|
||
}
|
||
if (giftEntryButton) {
|
||
const disabledByFeature = !featureEnabled("giftProps");
|
||
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.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;
|
||
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;
|
||
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) || "");
|
||
changeCountryButton.disabled = !featureEnabled("changeCountry") || !state.target || state.countriesLoading || state.changingCountry || !state.selectedCountryId || state.selectedCountryId === currentID;
|
||
changeCountryButton.textContent = state.changingCountry ? message("changing_country") : message("change_country");
|
||
}
|
||
if (banButton) banButton.disabled = !featureEnabled("banUser") || !canBanTarget(state.target);
|
||
if (unbanButton) unbanButton.disabled = !featureEnabled("unbanUser") || !canUnbanTarget(state.target);
|
||
}
|
||
|
||
async function loadProfile() {
|
||
const body = await requestGoJSON(endpointProfile());
|
||
state.manager = body?.manager || body || {};
|
||
state.features = normalizeFeatures(body?.features);
|
||
renderManager(state.manager);
|
||
updateActionState();
|
||
}
|
||
|
||
async function loadProps() {
|
||
state.props = [];
|
||
state.selectedPropsId = "";
|
||
renderProps();
|
||
if (!featureEnabled("giftProps")) {
|
||
setStatus($("#propsStatus"), message("gift_props_disabled"));
|
||
return;
|
||
}
|
||
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 loadCountries() {
|
||
state.countriesLoading = true;
|
||
setStatus($("#countryStatus"), message("loading"));
|
||
updateActionState();
|
||
try {
|
||
const body = await requestGoJSON(endpointCountries());
|
||
state.countries = Array.isArray(body?.records) ? body.records : [];
|
||
if (!state.selectedCountryId) state.selectedCountryId = String(state.target?.countryId || matchingCountryId(state.target) || "");
|
||
setStatus($("#countryStatus"), state.countries.length ? "" : message("no_countries"));
|
||
renderCountrySelect();
|
||
} finally {
|
||
state.countriesLoading = false;
|
||
updateActionState();
|
||
}
|
||
}
|
||
|
||
async function loadBDLeaders() {
|
||
state.bdLeadersLoading = true;
|
||
state.bdLeadersError = "";
|
||
renderBDLeaders();
|
||
try {
|
||
const body = await requestGoJSON(endpointBDLeaders());
|
||
state.bdLeaders = Array.isArray(body?.records) ? body.records : [];
|
||
setStatus($("#bdLeaderStatus"), "");
|
||
} catch (error) {
|
||
state.bdLeadersError = friendlyError(error);
|
||
throw error;
|
||
} finally {
|
||
state.bdLeadersLoading = false;
|
||
renderBDLeaders();
|
||
}
|
||
}
|
||
|
||
async function searchUser(event) {
|
||
event.preventDefault();
|
||
const input = $("#modalAccountInput");
|
||
const status = $("#modalSearchStatus");
|
||
const button = $("#modalSearchButton");
|
||
const account = String(input?.value || "").trim();
|
||
if (!account) {
|
||
setStatus(status, message("enter_user"), "error");
|
||
return;
|
||
}
|
||
|
||
button.disabled = true;
|
||
setStatus(status, message("searching"));
|
||
try {
|
||
const body = await requestGoJSON(endpointSearchUser(account));
|
||
state.target = body?.user || body || null;
|
||
renderTarget();
|
||
await prepareActiveActionAfterTarget();
|
||
setStatus(status, "");
|
||
} catch (error) {
|
||
state.target = null;
|
||
renderTarget();
|
||
setStatus(status, friendlyError(error), "error");
|
||
} finally {
|
||
button.disabled = false;
|
||
}
|
||
}
|
||
|
||
async function addBDLeader() {
|
||
if (!featureEnabled("addBdLeader")) {
|
||
showToast(message("add_bd_leader_disabled"));
|
||
return;
|
||
}
|
||
if (!state.target) {
|
||
setStatus($("#addBDLeaderStatus"), message("enter_user"), "error");
|
||
return;
|
||
}
|
||
if (state.addingBDLeader) return;
|
||
if (isTargetBDLeader()) {
|
||
setStatus($("#addBDLeaderStatus"), message("user_already_bd_leader"), "error");
|
||
return;
|
||
}
|
||
|
||
state.addingBDLeader = true;
|
||
setStatus($("#addBDLeaderStatus"), message("adding_bd_leader"));
|
||
updateActionState();
|
||
try {
|
||
await requestGoJSON(endpointBDLeaders(), {
|
||
method: "POST",
|
||
body: {
|
||
userId: displayUserID(state.target)
|
||
}
|
||
});
|
||
setStatus($("#addBDLeaderStatus"), "");
|
||
closeModals();
|
||
showToast(message("add_bd_leader_success"));
|
||
await loadBDLeaders();
|
||
} catch (error) {
|
||
setStatus($("#addBDLeaderStatus"), friendlyError(error), "error");
|
||
} finally {
|
||
state.addingBDLeader = false;
|
||
updateActionState();
|
||
}
|
||
}
|
||
|
||
async function sendProps() {
|
||
if (!featureEnabled("giftProps")) {
|
||
showToast(message("gift_props_disabled"));
|
||
return;
|
||
}
|
||
if (!state.target) {
|
||
setStatus($("#propsStatus"), message("enter_user"), "error");
|
||
return;
|
||
}
|
||
if (!state.selectedPropsId) return;
|
||
|
||
const button = $("#sendPropsButton");
|
||
button.disabled = true;
|
||
try {
|
||
await requestGoJSON(endpointSendProps(), {
|
||
method: "POST",
|
||
body: {
|
||
acceptUserId: displayUserID(state.target),
|
||
propsId: state.selectedPropsId
|
||
}
|
||
});
|
||
closeModals();
|
||
showToast(message("send_success"));
|
||
} catch (error) {
|
||
showToast(friendlyError(error));
|
||
} finally {
|
||
updateActionState();
|
||
}
|
||
}
|
||
|
||
async function banUser() {
|
||
if (!featureEnabled("banUser")) {
|
||
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");
|
||
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"), "");
|
||
closeModals();
|
||
showToast(message("ban_success"));
|
||
} catch (error) {
|
||
setStatus($("#banStatus"), friendlyError(error), "error");
|
||
} finally {
|
||
updateActionState();
|
||
}
|
||
}
|
||
|
||
async function unbanUser() {
|
||
if (!featureEnabled("unbanUser")) {
|
||
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");
|
||
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();
|
||
setStatus($("#unbanStatus"), "");
|
||
closeModals();
|
||
showToast(message("unban_success"));
|
||
} catch (error) {
|
||
setStatus($("#unbanStatus"), friendlyError(error), "error");
|
||
} finally {
|
||
updateActionState();
|
||
}
|
||
}
|
||
|
||
async function changeUserCountry() {
|
||
if (!state.target) {
|
||
setStatus($("#countryStatus"), message("enter_user"), "error");
|
||
return;
|
||
}
|
||
if (!featureEnabled("changeCountry")) {
|
||
setStatus($("#countryStatus"), message("change_country_disabled"), "error");
|
||
return;
|
||
}
|
||
if (!state.selectedCountryId) {
|
||
setStatus($("#countryStatus"), message("country_required"), "error");
|
||
return;
|
||
}
|
||
|
||
state.changingCountry = true;
|
||
setStatus($("#countryStatus"), message("changing_country"));
|
||
updateActionState();
|
||
try {
|
||
const body = await requestGoJSON(endpointChangeUserCountry(), {
|
||
method: "POST",
|
||
body: {
|
||
userId: displayUserID(state.target),
|
||
countryId: state.selectedCountryId
|
||
}
|
||
});
|
||
state.target = body?.user || state.target;
|
||
renderTarget();
|
||
setStatus($("#countryStatus"), "");
|
||
closeModals();
|
||
showToast(message("country_changed"));
|
||
} catch (error) {
|
||
setStatus($("#countryStatus"), friendlyError(error), "error");
|
||
} finally {
|
||
state.changingCountry = false;
|
||
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 === "manager_region_required") return message("manager_region_required");
|
||
if (code === "bd_leader_origin_mismatch") return message("bd_leader_origin_mismatch");
|
||
if (code === "user_is_admin") return message("user_is_admin");
|
||
if (code === "user_already_bd_leader") return message("user_already_bd_leader");
|
||
if (code === "user_already_bound_bd_leader") return message("user_already_bound_bd_leader");
|
||
if (code === "user_identity_locked") return message("user_identity_locked");
|
||
if (code === "country_not_found") return message("country_not_found");
|
||
if (code === "country_required") return message("country_required");
|
||
if (code === "prop_not_giftable") return message("prop_not_giftable");
|
||
if (code === "vip_not_giftable") return message("vip_not_giftable");
|
||
if (code === "manager_feature_disabled") return message("manager_feature_disabled");
|
||
return error?.message || message("request_failed");
|
||
}
|
||
|
||
function bindEvents() {
|
||
$(".back-button")?.addEventListener("click", closePage);
|
||
$(".language-button")?.addEventListener("click", (event) => {
|
||
event.stopPropagation();
|
||
setCountryMenuOpen(false);
|
||
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);
|
||
setCountryMenuOpen(false);
|
||
});
|
||
$("#addBDLeaderButton")?.addEventListener("click", () => openActionModal("addBDLeader"));
|
||
$("#bdLeaderListButton")?.addEventListener("click", () => openActionModal("bdLeaders"));
|
||
$("#refreshBDLeaderButton")?.addEventListener("click", () => {
|
||
loadBDLeaders().catch((error) => {
|
||
showToast(friendlyError(error));
|
||
});
|
||
});
|
||
$("#giftEntryButton")?.addEventListener("click", openGiftModal);
|
||
$("#countryEntryButton")?.addEventListener("click", openCountryModal);
|
||
$("#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);
|
||
setCountryMenuOpen(false);
|
||
setPropsTypeMenuOpen(Boolean($("#propsTypeMenu")?.hidden));
|
||
});
|
||
document.querySelectorAll("#propsTypeMenu [data-value]").forEach((button) => {
|
||
button.addEventListener("click", async (event) => {
|
||
event.stopPropagation();
|
||
await choosePropsType(button.dataset.value);
|
||
});
|
||
});
|
||
$("#countryButton")?.addEventListener("click", (event) => {
|
||
event.stopPropagation();
|
||
setLanguageMenuOpen(false);
|
||
setPropsTypeMenuOpen(false);
|
||
setCountryMenuOpen(Boolean($("#countryMenu")?.hidden));
|
||
});
|
||
document.addEventListener("keydown", (event) => {
|
||
if (event.key !== "Escape") return;
|
||
setLanguageMenuOpen(false);
|
||
setPropsTypeMenuOpen(false);
|
||
setCountryMenuOpen(false);
|
||
closeModals();
|
||
});
|
||
$("#modalSearchForm")?.addEventListener("submit", searchUser);
|
||
$("#addBDLeaderConfirmButton")?.addEventListener("click", addBDLeader);
|
||
$("#sendPropsButton")?.addEventListener("click", sendProps);
|
||
$("#banButton")?.addEventListener("click", banUser);
|
||
$("#unbanButton")?.addEventListener("click", unbanUser);
|
||
$("#changeCountryButton")?.addEventListener("click", changeUserCountry);
|
||
}
|
||
|
||
async function init() {
|
||
bindEvents();
|
||
renderPropsTypeSelect();
|
||
await applyLanguage(currentLang);
|
||
if (!readRawParam("token")) {
|
||
setLoading(false);
|
||
setStatus($("#pageStatus"), message("missing_token"), "error");
|
||
return;
|
||
}
|
||
|
||
try {
|
||
await loadProfile();
|
||
await Promise.all([loadProps(), loadBDLeaders()]);
|
||
} catch (error) {
|
||
showToast(friendlyError(error));
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}
|
||
|
||
init();
|
||
})();
|