2026-05-07 17:33:00 +08:00

1506 lines
55 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 fallbackMessages = {
en: {
document_title: "BD Leader Center",
page_label: "BD Leader Center",
back: "Back",
title: "BD Leader Center",
language_button_aria: "Change language",
role: "BD Leader",
uid_prefix: "UID:",
my_available_salary: "Salary",
details: "Withdraw/Exchange",
bd_number: "BD number",
team_salary: "Team salary",
bd_leader_history: "Salary History",
agency_list_link: "Agency List",
bd_list_link: "BD List",
invite_become_agent: "Invite Agent",
invite_become_bd: "Invite BD",
invite_agent_title: "Invite Agent",
invite_bd_title: "Invite BD",
enter_user_id: "Enter User ID",
search: "Search",
searching: "Searching...",
invite: "Invite",
inviting: "Inviting...",
cancel: "Cancel",
success: "Success",
bd_list_preview: "BD List",
bd_list_preview_subtitle: "Current settlement period",
more: "More",
total_income: "Total income",
previous_period_income: "Previous period income",
salary: "Salary",
agencies: "Agencies",
agency_number: "Agency number",
team_recharge: "Team recharge",
recharge: "Recharge",
host: "Host",
loading_data: "Loading...",
no_data: "No data",
in_progress: "In Progress",
pending: "Pending",
completed: "Completed",
out_of_account: "Out of account",
no_users_found: "No users found",
invitation_submitted: "Invitation submitted",
application_cancelled: "Application cancelled",
something_went_wrong: "Something went wrong",
failed_to_load_invites: "Failed to load invitations",
cancellation_failed_try_again: "Cancellation failed. Try again.",
close: "Close",
failed_to_load: "Failed to load data",
failed_to_load_history: "Failed to load history"
},
ar: {
document_title: "مركز قائد BD",
page_label: "مركز قائد BD",
back: "رجوع",
title: "مركز قائد BD",
language_button_aria: "تغيير اللغة",
role: "قائد BD",
uid_prefix: "UID:",
my_available_salary: "الراتب",
details: "سحب/تبديل",
bd_number: "عدد BD",
team_salary: "راتب الفريق",
bd_leader_history: "سجل الراتب",
agency_list_link: "قائمة الوكالات",
bd_list_link: "قائمة BD",
invite_become_agent: "دعوة وكيل",
invite_become_bd: "دعوة BD",
invite_agent_title: "دعوة وكيل",
invite_bd_title: "دعوة BD",
enter_user_id: "أدخل معرف المستخدم",
search: "بحث",
searching: "جار البحث...",
invite: "دعوة",
inviting: "جار الدعوة...",
cancel: "إلغاء",
success: "نجاح",
bd_list_preview: "قائمة BD",
bd_list_preview_subtitle: "فترة التسوية الحالية",
more: "المزيد",
total_income: "إجمالي الدخل",
previous_period_income: "دخل الفترة السابقة",
salary: "الراتب",
agencies: "الوكالات",
loading_data: "جار التحميل...",
no_data: "لا توجد بيانات",
in_progress: "قيد التنفيذ",
pending: "قيد الانتظار",
completed: "مكتمل",
out_of_account: "خارج الحساب",
no_users_found: "لم يتم العثور على مستخدمين",
invitation_submitted: "تم إرسال الدعوة",
application_cancelled: "تم إلغاء الطلب",
something_went_wrong: "حدث خطأ ما",
failed_to_load_invites: "فشل تحميل الدعوات",
cancellation_failed_try_again: "فشل الإلغاء. حاول مرة أخرى.",
close: "إغلاق",
failed_to_load: "فشل تحميل البيانات",
failed_to_load_history: "فشل تحميل السجل"
},
tr: {
document_title: "BD Leader Merkezi",
page_label: "BD Leader Merkezi",
back: "Geri",
title: "BD Leader Merkezi",
language_button_aria: "Dili değiştir",
role: "BD Leader",
uid_prefix: "UID:",
my_available_salary: "Maaş",
details: "Withdraw/Exchange",
bd_number: "BD sayısı",
team_salary: "Takım maaşı",
bd_leader_history: "Maaş Geçmişi",
agency_list_link: "Ajans listesi",
bd_list_link: "BD Listesi",
invite_become_agent: "Ajans Davet Et",
invite_become_bd: "BD Davet Et",
invite_agent_title: "Ajans Davet Et",
invite_bd_title: "BD Davet Et",
enter_user_id: "Kullanıcı ID gir",
search: "Ara",
searching: "Aranıyor...",
invite: "Davet et",
inviting: "Davet ediliyor...",
cancel: "İptal",
success: "Başarılı",
bd_list_preview: "BD Listesi",
bd_list_preview_subtitle: "Mevcut ödeme dönemi",
more: "Daha fazla",
total_income: "Toplam gelir",
previous_period_income: "Önceki dönem geliri",
salary: "Maaş",
agencies: "Ajanslar",
loading_data: "Yükleniyor...",
no_data: "Veri yok",
in_progress: "Devam ediyor",
pending: "Beklemede",
completed: "Tamamlandı",
out_of_account: "Hesap dışı",
no_users_found: "Kullanıcı bulunamadı",
invitation_submitted: "Davet gönderildi",
application_cancelled: "Başvuru iptal edildi",
something_went_wrong: "Bir şeyler ters gitti",
failed_to_load_invites: "Davetler yüklenemedi",
cancellation_failed_try_again: "İptal başarısız. Tekrar deneyin.",
close: "Kapat",
failed_to_load: "Veri yüklenemedi",
failed_to_load_history: "Geçmiş yüklenemedi"
},
id: {
document_title: "Pusat BD Leader",
page_label: "Pusat BD Leader",
back: "Kembali",
title: "Pusat BD Leader",
language_button_aria: "Ubah bahasa",
role: "BD Leader",
uid_prefix: "UID:",
my_available_salary: "Gaji",
details: "Withdraw/Exchange",
bd_number: "Jumlah BD",
team_salary: "Gaji tim",
bd_leader_history: "Riwayat Gaji",
agency_list_link: "Daftar agensi",
bd_list_link: "Daftar BD",
invite_become_agent: "Invite Agent",
invite_become_bd: "Invite BD",
invite_agent_title: "Invite Agent",
invite_bd_title: "Invite BD",
enter_user_id: "Masukkan ID Pengguna",
search: "Cari",
searching: "Mencari...",
invite: "Undang",
inviting: "Mengundang...",
cancel: "Batal",
success: "Berhasil",
bd_list_preview: "Daftar BD",
bd_list_preview_subtitle: "Periode settlement saat ini",
more: "Lainnya",
total_income: "Total pendapatan",
previous_period_income: "Pendapatan periode sebelumnya",
salary: "Gaji",
agencies: "Agensi",
loading_data: "Memuat...",
no_data: "Tidak ada data",
in_progress: "Sedang diproses",
pending: "Menunggu",
completed: "Selesai",
out_of_account: "Di luar akun",
no_users_found: "Pengguna tidak ditemukan",
invitation_submitted: "Undangan terkirim",
application_cancelled: "Pengajuan dibatalkan",
something_went_wrong: "Terjadi kesalahan",
failed_to_load_invites: "Gagal memuat undangan",
cancellation_failed_try_again: "Gagal membatalkan. Coba lagi.",
close: "Tutup",
failed_to_load: "Gagal memuat data",
failed_to_load_history: "Gagal memuat riwayat"
}
};
const params = readURLParams();
let currentLang = normalizeLanguage(params.get("lang"));
let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
const state = {
profile: null,
identity: null,
teamEntry: null,
balanceTotal: {},
bill: {},
agencyBill: null,
history: null,
historyMore: null,
teamList: {
type: "bd",
loading: false,
error: ""
},
invite: {
mode: "agent",
invited: [],
searched: [],
showSearch: false,
query: "",
locked: false,
searching: false,
statusText: "",
statusType: ""
}
};
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();
if (value.startsWith("ar")) return "ar";
if (value.startsWith("tr")) return "tr";
if (value.startsWith("id")) return "id";
return supportedLanguages.includes(value) ? value : "en";
}
function message(key, fallback = "") {
return currentMessages[key] || fallbackMessages.en[key] || fallback || key;
}
function renderMessages(messages) {
document.querySelectorAll("[data-i18n]").forEach((node) => {
const value = messages[node.dataset.i18n] || fallbackMessages.en[node.dataset.i18n];
if (value) node.textContent = value;
});
document.querySelectorAll("[data-i18n-aria]").forEach((node) => {
const value = messages[node.dataset.i18nAria] || fallbackMessages.en[node.dataset.i18nAria];
if (value) node.setAttribute("aria-label", value);
});
document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => {
const value = messages[node.dataset.i18nPlaceholder] || fallbackMessages.en[node.dataset.i18nPlaceholder];
if (value) node.setAttribute("placeholder", value);
});
}
function apiBaseURL() {
const override = window.HYAPP_API_BASE_URL || params.get("apiBase");
if (override) return String(override).replace(/\/+$/, "");
return API_BASE_URL.replace(/\/+$/, "");
}
function normalizeAuthorization(token) {
const value = String(token || "").trim();
if (!value) return "";
return /^bearer\s+/i.test(value) ? value : `Bearer ${value}`;
}
function authHeadersFromToken(token) {
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=ATYOU;originChild=ATYOU"
};
}
async function requestJSON(path, options = {}) {
const token = readRawParam("token");
if (!token) throw new Error("Missing token");
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(new URL(path, `${apiBaseURL()}/`).toString(), 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.response = data;
throw error;
}
return data.body ?? data.data ?? null;
}
function setLoading(isLoading) {
const root = document.querySelector(".bd-leader-center");
const mask = document.querySelector("#loadingMask");
if (root) root.dataset.loading = isLoading ? "true" : "false";
if (mask) mask.hidden = !isLoading;
}
function updateModalLock() {
const hasOpen = Array.from(document.querySelectorAll(".history-modal, .details-modal, .invite-modal")).some((modal) => !modal.hidden);
document.body.classList.toggle("modal-open", hasOpen);
}
function setModalOpen(selector, open) {
const modal = document.querySelector(selector);
if (!modal) return;
modal.hidden = !open;
updateModalLock();
}
function showToast(text) {
const toast = document.querySelector("#toast");
if (!toast) return;
toast.textContent = text || "";
toast.hidden = !text;
window.clearTimeout(showToast.timer);
showToast.timer = window.setTimeout(() => {
toast.hidden = true;
}, 2500);
}
function formatMoney(value) {
const number = Number(value);
if (!Number.isFinite(number)) return "0.00";
return number.toFixed(2);
}
function formatCount(value) {
if (value === null || value === undefined || value === "") return "0";
const number = Number(value);
if (Number.isFinite(number) && Number.isInteger(number)) return String(number);
if (Number.isFinite(number)) return String(Number(number.toFixed(2)));
return String(value);
}
function pickSpecialAccount(profile) {
return profile?.ownSpecialId?.account || profile?.specialId?.account || profile?.specialAccount || profile?.shortAccount || profile?.shortId || "";
}
function pickProfileName(profile) {
return profile?.userNickname || profile?.name || "-";
}
function pickProfileAccount(profile) {
return pickSpecialAccount(profile) || profile?.account || profile?.id || "-";
}
function renderAvatar(profile) {
const image = document.querySelector("#profileAvatar");
const fallback = document.querySelector("#profileAvatarFallback");
if (!image || !fallback) return;
const name = pickProfileName(profile);
const avatar = profile?.userAvatar || profile?.avatar || "";
fallback.textContent = String(name || "B").slice(0, 1).toUpperCase() || "B";
if (!avatar) {
image.hidden = true;
image.removeAttribute("src");
fallback.hidden = false;
return;
}
image.alt = name;
fallback.hidden = true;
image.hidden = false;
image.onerror = () => {
image.hidden = true;
image.removeAttribute("src");
fallback.hidden = false;
};
image.src = avatar;
}
function renderProfile(profile = state.profile) {
const name = pickProfileName(profile);
const account = pickProfileAccount(profile);
const nameNode = document.querySelector("#profileName");
const uidNode = document.querySelector("#profileUid");
if (nameNode) nameNode.textContent = name;
if (uidNode) uidNode.textContent = `${message("uid_prefix")} ${account}`.trim();
renderAvatar(profile);
}
function renderBalance() {
const balance = state.balanceTotal?.availableBalance ?? state.balanceTotal?.balance ?? 0;
const node = document.querySelector("#availableBalance");
if (node) node.textContent = formatMoney(balance);
}
function balanceAmount(balance) {
const value = Number(balance?.availableBalance ?? balance?.balance ?? balance?.currentSalary ?? 0);
return Number.isFinite(value) ? value : 0;
}
function renderBill() {
const bill = state.bill || {};
const members = Array.isArray(bill.memberBillList) ? bill.memberBillList : [];
const bdNumber = document.querySelector("#bdNumber");
const teamSalary = document.querySelector("#teamSalary");
if (bdNumber) bdNumber.textContent = formatCount(bill.memberCount ?? bill.bdNumber ?? members.length);
if (teamSalary) teamSalary.textContent = formatMoney(bill.totalSalary ?? bill.teamSalary ?? 0);
renderMembers(members);
}
function createFallbackAvatar(name) {
const span = document.createElement("span");
span.className = "member-fallback";
span.textContent = String(name || "B").slice(0, 1).toUpperCase() || "B";
return span;
}
function createMemberRow(item, options = {}) {
const type = options.type || "bd";
const name = item?.agentName || item?.bdUserName || "-";
const account = item?.agentAccount || item?.account || "-";
const row = document.createElement("article");
row.className = "member-row";
const avatar = document.createElement("div");
avatar.className = "member-avatar";
const avatarUrl = item?.agentAvatar || item?.bdUserAvatar || "";
if (avatarUrl) {
const image = document.createElement("img");
image.src = avatarUrl;
image.alt = "";
image.addEventListener("error", () => {
avatar.replaceChildren(createFallbackAvatar(name));
});
avatar.appendChild(image);
} else {
avatar.appendChild(createFallbackAvatar(name));
}
const main = document.createElement("div");
main.className = "member-main";
const nameNode = document.createElement("div");
nameNode.className = "member-name";
nameNode.textContent = name;
const metaNode = document.createElement("div");
metaNode.className = "member-meta";
metaNode.textContent = `${message("uid_prefix")} ${account}`.trim();
main.append(nameNode, metaNode);
const side = document.createElement("div");
side.className = "member-side";
const sideLines = type === "agency"
? [
`${message("team_salary")}: $${formatMoney(item?.teamSalaryAmount || 0)}`,
`${message("recharge")}: $${formatMoney(item?.teamRechargeAmount || 0)}`,
`${message("host")}: ${formatCount(item?.teamMemberCount || 0)}`
]
: [
`${message("team_salary")}: $${formatMoney(item?.teamSalaryAmount || 0)}`,
`${message("agencies")}: ${formatCount(item?.teamMemberCount || item?.agencyCount || 0)}`
];
sideLines.forEach((line) => {
const itemNode = document.createElement("div");
itemNode.textContent = line;
side.appendChild(itemNode);
});
row.append(avatar, main, side);
return row;
}
function replaceChildren(node, children) {
while (node.firstChild) node.removeChild(node.firstChild);
children.forEach((child) => node.appendChild(child));
}
function renderMembers(members) {
const list = document.querySelector("#bdMemberList");
if (!list) return;
const preview = members.slice(0, 5);
if (!preview.length) {
const empty = document.createElement("div");
empty.className = "empty-state";
empty.textContent = message("no_data");
replaceChildren(list, [empty]);
return;
}
replaceChildren(list, preview.map(createMemberRow));
}
function normalizeTeamListType(type) {
return type === "agency" ? "agency" : "bd";
}
function teamListBill(type = state.teamList.type) {
return normalizeTeamListType(type) === "agency" ? state.agencyBill : state.bill;
}
function hasTeamListData(data) {
return Boolean(data && (Array.isArray(data.memberBillList) || data.billTitle || data.memberCount !== undefined));
}
function createSummaryCell(label, value) {
const cell = document.createElement("div");
const labelNode = document.createElement("span");
labelNode.textContent = label;
const valueNode = document.createElement("strong");
valueNode.textContent = value;
cell.append(labelNode, valueNode);
return cell;
}
function renderTeamListCard() {
const type = normalizeTeamListType(state.teamList.type);
const data = teamListBill(type) || {};
const members = Array.isArray(data.memberBillList) ? data.memberBillList : [];
const summary = document.querySelector("#teamListSummary");
const content = document.querySelector("#teamListContent");
document.querySelectorAll("[data-list-tab]").forEach((button) => {
const active = normalizeTeamListType(button.dataset.listTab) === type;
button.classList.toggle("is-active", active);
button.setAttribute("aria-selected", String(active));
});
if (summary) {
const cells = type === "agency"
? [
createSummaryCell(message("agency_number"), formatCount(data.memberCount ?? members.length)),
createSummaryCell(message("team_salary"), `$${formatMoney(data.totalSalary || 0)}`),
createSummaryCell(message("team_recharge"), `$${formatMoney(data.totalRecharge || 0)}`)
]
: [
createSummaryCell(message("bd_number"), formatCount(data.memberCount ?? members.length)),
createSummaryCell(message("team_salary"), `$${formatMoney(data.totalSalary || 0)}`)
];
replaceChildren(summary, cells);
}
if (!content) return;
if (state.teamList.loading) {
const loading = document.createElement("div");
loading.className = "empty-state";
loading.textContent = message("loading_data");
replaceChildren(content, [loading]);
return;
}
if (state.teamList.error) {
const error = document.createElement("div");
error.className = "empty-state";
error.textContent = state.teamList.error;
replaceChildren(content, [error]);
return;
}
if (!members.length) {
const empty = document.createElement("div");
empty.className = "empty-state";
empty.textContent = message("no_data");
replaceChildren(content, [empty]);
return;
}
replaceChildren(content, members.map((item) => createMemberRow(item, { type })));
}
function normalizeStatus(status) {
if (status === "UNPAID" || status === "In Progress" || status === message("in_progress")) return message("in_progress");
if (status === "Pending" || status === message("pending")) return message("pending");
if (status === "SETTLED" || status === "PAY_OUT" || status === "Completed" || status === message("completed")) return message("completed");
if (status === "HANG_UP" || status === "Out of account" || status === "Out Of Account" || status === message("out_of_account")) return message("out_of_account");
return status || "";
}
function statusClass(status) {
const value = normalizeStatus(status);
if (value === message("completed")) return "is-completed";
if (value === message("pending")) return "is-pending";
if (value === message("out_of_account")) return "is-out";
return "is-progress";
}
function isCompletedStatus(status) {
return normalizeStatus(status) === message("completed");
}
function createHistoryCell(label, value) {
const cell = document.createElement("div");
cell.className = "history-cell";
const labelNode = document.createElement("span");
labelNode.textContent = label;
const valueNode = document.createElement("strong");
valueNode.textContent = value;
cell.append(labelNode, valueNode);
return cell;
}
function createHistoryRow(item) {
const row = document.createElement("article");
row.className = `history-row ${statusClass(item?.statusText)}`;
const status = document.createElement("div");
status.className = "status-tag";
status.textContent = normalizeStatus(item?.statusText);
const title = document.createElement("div");
title.className = "history-title";
title.textContent = item?.billTitle || "-";
const grid = document.createElement("div");
grid.className = "history-grid";
grid.append(
createHistoryCell(message("bd_number"), formatCount(item?.bdNumber || 0)),
createHistoryCell(message("team_salary"), `$${formatMoney(item?.teamSalaryAmount || 0)}`)
);
row.append(status, title, grid);
if (isCompletedStatus(item?.statusText)) {
const more = document.createElement("button");
more.className = "more-button";
more.type = "button";
more.textContent = message("more");
more.addEventListener("click", () => showHistoryMore(item));
row.appendChild(more);
}
return row;
}
function renderHistory() {
const history = state.history || {};
const total = document.querySelector("#historyTotalIncome");
const previous = document.querySelector("#historyPreviousIncome");
const list = document.querySelector("#historyList");
if (total) total.textContent = `$${formatMoney(history.totalIncome || 0)}`;
if (previous) previous.textContent = `$${formatMoney(history.previousPeriodIncome || 0)}`;
if (!list) return;
const items = Array.isArray(history.incomeDetails) ? history.incomeDetails : [];
if (!items.length) {
const empty = document.createElement("div");
empty.className = "empty-state";
empty.textContent = message("no_data");
replaceChildren(list, [empty]);
return;
}
replaceChildren(list, items.map(createHistoryRow));
}
const digitMaps = [
["\u0660", "0"], ["\u0661", "1"], ["\u0662", "2"], ["\u0663", "3"], ["\u0664", "4"], ["\u0665", "5"], ["\u0666", "6"], ["\u0667", "7"], ["\u0668", "8"], ["\u0669", "9"],
["\u06f0", "0"], ["\u06f1", "1"], ["\u06f2", "2"], ["\u06f3", "3"], ["\u06f4", "4"], ["\u06f5", "5"], ["\u06f6", "6"], ["\u06f7", "7"], ["\u06f8", "8"], ["\u06f9", "9"],
["\u09e6", "0"], ["\u09e7", "1"], ["\u09e8", "2"], ["\u09e9", "3"], ["\u09ea", "4"], ["\u09eb", "5"], ["\u09ec", "6"], ["\u09ed", "7"], ["\u09ee", "8"], ["\u09ef", "9"]
];
const monthNames = new Map(Object.entries({
january: 1, jan: 1, "يناير": 1, "كانون الثاني": 1, ocak: 1, "জানুয়ারি": 1,
february: 2, feb: 2, "فبراير": 2, "شباط": 2, subat: 2, "şubat": 2, "ফেব্রুয়ারি": 2,
march: 3, mar: 3, "مارس": 3, "آذار": 3, mart: 3, "মার্চ": 3,
april: 4, apr: 4, "أبريل": 4, "ابريل": 4, "نيسان": 4, nisan: 4, "এপ্রিল": 4,
may: 5, "مايو": 5, "أيار": 5, mayis: 5, "mayıs": 5, "মে": 5,
june: 6, jun: 6, "يونيو": 6, "حزيران": 6, haziran: 6, "জুন": 6,
july: 7, jul: 7, "يوليو": 7, "تموز": 7, temmuz: 7, "জুলাই": 7,
august: 8, aug: 8, "أغسطس": 8, "اغسطس": 8, "آب": 8, agustos: 8, "ağustos": 8, "আগস্ট": 8,
september: 9, sep: 9, "سبتمبر": 9, "أيلول": 9, eylul: 9, "eylül": 9, "সেপ্টেম্বর": 9,
october: 10, oct: 10, "أكتوبر": 10, "اكتوبر": 10, "تشرين الأول": 10, ekim: 10, "অক্টোবর": 10,
november: 11, nov: 11, "نوفمبر": 11, "تشرين الثاني": 11, kasim: 11, "kasım": 11, "নভেম্বর": 11,
december: 12, dec: 12, "ديسمبر": 12, "كانون الأول": 12, aralik: 12, "aralık": 12, "ডিসেম্বর": 12
}));
function pad2(value) {
return String(value).padStart(2, "0");
}
function formatBillBelong(year, month, day, time) {
const monthNumber = Number(month);
const dayNumber = Number(day);
if (monthNumber < 1 || monthNumber > 12 || dayNumber < 1 || dayNumber > 31) return "";
return `${year}-${pad2(monthNumber)}-${pad2(dayNumber)}T${time}`;
}
function prefersDayMonthOrder() {
return currentLang === "ar" || currentLang === "tr" || currentLang === "id";
}
function formatNumericBillBelong(year, firstPart, secondPart, time) {
const first = Number(firstPart);
const second = Number(secondPart);
if (first > 12 && second <= 12) return formatBillBelong(year, second, first, time);
if (second > 12 && first <= 12) return formatBillBelong(year, first, second, time);
return prefersDayMonthOrder()
? formatBillBelong(year, second, first, time)
: formatBillBelong(year, first, second, time);
}
function normalizeDateText(value) {
return digitMaps.reduce((textValue, [from, to]) => textValue.split(from).join(to), String(value || ""))
.replace(/&amp;/gi, "&")
.replace(/&(?:rlm|lrm);/gi, "")
.replace(/&nbsp;/gi, " ")
.replace(/[\u061c\u200e\u200f]/g, "")
.replace(/[،]/g, ",")
.replace(/\s+/g, " ")
.trim();
}
function normalizeTime(value, markerText = "") {
const match = String(value || "").match(/(\d{1,2}):(\d{2})(?::(\d{2}))?/);
if (!match) return "00:00:00";
let hour = Number(match[1]);
const marker = markerText.toLowerCase();
const isPm = /\bpm\b|مساء|(?:^|\s)م(?:\s|$)/.test(marker);
const isAm = /\bam\b|صباح|(?:^|\s)ص(?:\s|$)/.test(marker);
if (isPm && hour < 12) hour += 12;
if (isAm && hour === 12) hour = 0;
return `${pad2(hour)}:${match[2]}:${match[3] || "00"}`;
}
function normalizeBillBelong(value) {
const textValue = normalizeDateText(value);
if (!textValue) return "";
if (/^\d{6,8}$/.test(textValue)) return textValue;
const isoMatch = textValue.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/);
if (isoMatch) {
return formatBillBelong(isoMatch[1], isoMatch[2], isoMatch[3], `${isoMatch[4] || "00"}:${isoMatch[5] || "00"}:${isoMatch[6] || "00"}`);
}
const compactDateTime = textValue.match(/^(\d{4})(\d{2})(\d{2})(?:\D?(\d{2})(\d{2})(\d{2})?)?$/);
if (compactDateTime) {
return formatBillBelong(compactDateTime[1], compactDateTime[2], compactDateTime[3], `${compactDateTime[4] || "00"}:${compactDateTime[5] || "00"}:${compactDateTime[6] || "00"}`);
}
const numericDate = textValue.match(/\b(?:(\d{4})\s*[./-]\s*(\d{1,2})\s*[./-]\s*(\d{1,2})|(\d{1,2})\s*[./-]\s*(\d{1,2})\s*[./-]\s*(\d{4}))\b/);
if (numericDate) {
const time = normalizeTime(textValue, textValue);
if (numericDate[1]) return formatBillBelong(numericDate[1], numericDate[2], numericDate[3], time);
return formatNumericBillBelong(numericDate[6], numericDate[4], numericDate[5], time);
}
const lowerText = textValue.toLowerCase();
let month = 0;
for (const [name, valueForMonth] of monthNames) {
if (lowerText.includes(name)) {
month = valueForMonth;
break;
}
}
const yearMatch = textValue.match(/\b(20\d{2})\b/);
if (month && yearMatch) {
const withoutYear = textValue.replace(yearMatch[1], "");
const dayMatch = withoutYear.match(/\b([0-3]?\d)\b/);
if (dayMatch) {
return formatBillBelong(yearMatch[1], month, dayMatch[1], normalizeTime(textValue, textValue));
}
}
return "";
}
function historyMorePath(source) {
const normalized = normalizeBillBelong(source?.billBelong ?? source?.period ?? source?.billTitle ?? source);
return normalized ? `/team/bd/leader/history/more?billBelong=${encodeURIComponent(normalized)}` : "";
}
function createDetailsCell(label, value) {
const cell = document.createElement("div");
cell.className = "details-cell";
const labelNode = document.createElement("span");
labelNode.textContent = label;
const valueNode = document.createElement("strong");
valueNode.textContent = value;
cell.append(labelNode, valueNode);
return cell;
}
function renderDetails() {
const data = state.historyMore || {};
const title = document.querySelector("#detailsTitle");
const summary = document.querySelector("#detailsSummary");
const list = document.querySelector("#detailsList");
if (title) title.textContent = data.period || "-";
if (summary) {
replaceChildren(summary, [
createDetailsCell(message("team_salary"), `$${formatMoney(data.teamSalary || 0)}`),
createDetailsCell(message("bd_number"), formatCount(data.bdNumber || 0))
]);
}
if (!list) return;
const members = Array.isArray(data.memberDetails) ? data.memberDetails : [];
if (!members.length) {
const empty = document.createElement("div");
empty.className = "empty-state";
empty.textContent = message("no_data");
replaceChildren(list, [empty]);
return;
}
replaceChildren(list, members.map((item) => {
const row = createMemberRow({
agentName: item.bdUserName,
agentAvatar: item.bdUserAvatar,
agentAccount: item.account,
teamSalaryAmount: item.salary,
teamMemberCount: item.agencyCount
});
row.classList.add("details-row");
return row;
}));
}
function inviteConfig(mode = state.invite.mode) {
if (mode === "bd") {
return {
titleKey: "invite_bd_title",
list: "/team/bd/invite-bd-message-own",
invite: "/team/bd/invite-bd",
cancel: (id) => `/team/bd/invite-bd-cancel/${encodeURIComponent(id)}`
};
}
return {
titleKey: "invite_agent_title",
list: "/team/bd/invite-message-own",
invite: "/team/bd/invite-agent",
cancel: (id) => `/team/bd/invite-agent-cancel/${encodeURIComponent(id)}`
};
}
function normalizeInviteMode(mode) {
return mode === "bd" ? "bd" : "agent";
}
function inviteItemProfile(item) {
return item?.userProfile || item?.profile || item?.inviteUserProfile || item || {};
}
function inviteProfileId(profile) {
return String(profile?.id || profile?.userId || "");
}
function sameInviteProfile(left, right) {
const leftId = inviteProfileId(left);
const rightId = inviteProfileId(right);
if (leftId && rightId) return leftId === rightId;
const leftAccount = String(left?.account || pickSpecialAccount(left) || "");
const rightAccount = String(right?.account || pickSpecialAccount(right) || "");
return Boolean(leftAccount && rightAccount && leftAccount === rightAccount);
}
function inviteStatusType(status) {
const value = String(status ?? "").toUpperCase();
if (status === 0 || value === "0" || value === "PENDING") return "pending";
if (status === 1 || value === "1" || value === "SUCCESS" || value === "ACCEPTED") return "success";
return "";
}
function mergeInviteStatus(profile) {
const record = state.invite.invited.find((item) => sameInviteProfile(inviteItemProfile(item), profile));
if (!record) return { userProfile: profile };
return {
...record,
userProfile: {
...profile,
...inviteItemProfile(record)
}
};
}
function setInviteStatus(text = "", type = "") {
state.invite.statusText = text;
state.invite.statusType = type;
const status = document.querySelector("#inviteStatus");
if (!status) return;
status.textContent = text;
status.hidden = !text;
status.classList.toggle("is-error", type === "error");
status.classList.toggle("is-success", type === "success");
}
function createInviteFallback(name) {
const span = document.createElement("span");
span.className = "invite-avatar-fallback";
span.textContent = String(name || "B").slice(0, 1).toUpperCase() || "B";
return span;
}
function createInviteAvatar(profile) {
const avatar = document.createElement("div");
avatar.className = "invite-user-avatar";
const name = pickProfileName(profile);
const avatarUrl = profile?.userAvatar || profile?.avatar || "";
if (!avatarUrl) {
avatar.appendChild(createInviteFallback(name));
return avatar;
}
const image = document.createElement("img");
image.src = avatarUrl;
image.alt = "";
image.addEventListener("error", () => {
avatar.replaceChildren(createInviteFallback(name));
});
avatar.appendChild(image);
return avatar;
}
function createInviteCard(item) {
const profile = inviteItemProfile(item);
const status = inviteStatusType(item?.status);
const card = document.createElement("article");
card.className = "invite-user-card";
const main = document.createElement("div");
main.className = "invite-user-main";
const nameNode = document.createElement("div");
nameNode.className = "invite-user-name";
nameNode.textContent = pickProfileName(profile);
const metaNode = document.createElement("div");
metaNode.className = "invite-user-meta";
metaNode.textContent = `${message("uid_prefix")} ${pickProfileAccount(profile)}`.trim();
main.append(nameNode, metaNode);
const side = document.createElement("div");
side.className = "invite-user-side";
if (status === "pending") {
const badge = document.createElement("span");
badge.className = "invite-badge pending";
badge.textContent = message("pending");
const cancel = document.createElement("button");
cancel.className = "invite-cancel";
cancel.type = "button";
cancel.textContent = message("cancel");
cancel.disabled = state.invite.locked;
cancel.addEventListener("click", () => cancelInvite(item?.id));
side.append(badge, cancel);
} else if (status === "success") {
const badge = document.createElement("span");
badge.className = "invite-badge success";
badge.textContent = message("success");
side.appendChild(badge);
} else {
const invite = document.createElement("button");
invite.className = "invite-action";
invite.type = "button";
invite.textContent = state.invite.locked ? message("inviting") : message("invite");
invite.disabled = state.invite.locked || !inviteProfileId(profile);
invite.addEventListener("click", () => inviteUser(profile));
side.appendChild(invite);
}
card.append(createInviteAvatar(profile), main, side);
return card;
}
function renderInviteModal() {
const config = inviteConfig();
const title = document.querySelector("#inviteModalTitle");
const input = document.querySelector("#inviteSearchInput");
const searchButton = document.querySelector("#inviteSearchButton");
const list = document.querySelector("#inviteList");
const empty = document.querySelector("#inviteEmpty");
const rows = state.invite.showSearch ? state.invite.searched : state.invite.invited;
const hasQuery = Boolean(String(state.invite.query || "").trim());
if (title) title.textContent = message(config.titleKey);
if (input && document.activeElement !== input) input.value = state.invite.query;
if (searchButton) {
searchButton.disabled = state.invite.locked || !hasQuery;
searchButton.textContent = state.invite.searching ? message("searching") : message("search");
}
setInviteStatus(state.invite.statusText, state.invite.statusType);
if (list) replaceChildren(list, rows.map(createInviteCard));
if (empty) empty.hidden = rows.length > 0 || Boolean(state.invite.statusText);
}
async function loadInviteRecords(options = {}) {
const config = inviteConfig();
if (!options.silent) setInviteStatus(message("loading_data"));
renderInviteModal();
try {
const records = await requestJSON(config.list);
state.invite.invited = Array.isArray(records) ? records : [];
if (!options.preserveStatus) setInviteStatus("");
} catch (error) {
console.error("Failed to load invite records:", error);
setInviteStatus(error.response?.errorMsg || error.message || message("failed_to_load_invites"), "error");
} finally {
renderInviteModal();
}
}
function handleInviteInput(event) {
const query = String(event?.target?.value || "");
state.invite.query = query;
if (!query.trim()) {
state.invite.showSearch = false;
state.invite.searched = [];
setInviteStatus("");
}
renderInviteModal();
}
async function searchInviteProfile(query) {
let profile = null;
let firstError = null;
try {
profile = await requestJSON(`/user/user-profile/open-search?account=${encodeURIComponent(query)}`);
} catch (error) {
firstError = error;
}
if (!profile && /^\d+$/.test(query)) {
try {
profile = await requestJSON(`/user/user-profile?userId=${encodeURIComponent(query)}`);
firstError = null;
} catch (error) {
if (!firstError) firstError = error;
}
}
const primaryProfile = Array.isArray(profile) ? profile[0] : profile;
const normalizedProfile = inviteItemProfile(primaryProfile);
if (normalizedProfile && (!pickProfileName(normalizedProfile) || pickProfileName(normalizedProfile) === "-" || !pickSpecialAccount(normalizedProfile) || !normalizedProfile.userAvatar)) {
const userId = inviteProfileId(normalizedProfile) || (/^\d+$/.test(query) ? query : "");
if (userId) {
try {
const detail = await requestJSON(`/user/user-profile?userId=${encodeURIComponent(userId)}`);
const mergedProfile = { ...normalizedProfile, ...inviteItemProfile(detail) };
profile = Array.isArray(profile)
? [{ ...primaryProfile, userProfile: mergedProfile }, ...profile.slice(1)]
: (primaryProfile?.userProfile ? { ...primaryProfile, userProfile: mergedProfile } : mergedProfile);
} catch (error) {
console.debug("Invite search detail fallback skipped:", error);
}
}
}
if (!profile && firstError) throw firstError;
return Array.isArray(profile) ? profile : (profile ? [profile] : []);
}
async function searchInviteUser(event) {
if (event) event.preventDefault();
if (state.invite.locked) return;
const input = document.querySelector("#inviteSearchInput");
const query = String(input?.value || "").trim();
state.invite.query = query;
state.invite.showSearch = Boolean(query);
state.invite.searched = [];
if (!query) {
setInviteStatus("");
renderInviteModal();
return;
}
state.invite.locked = true;
state.invite.searching = true;
setInviteStatus(message("searching"));
renderInviteModal();
try {
const profiles = await searchInviteProfile(query);
state.invite.searched = profiles
.map((profile) => inviteItemProfile(profile))
.filter((profile) => inviteProfileId(profile) || profile?.account)
.map(mergeInviteStatus);
setInviteStatus("");
} catch (error) {
console.error("Invite search failed:", error);
setInviteStatus(error.response?.errorMsg || error.message || message("something_went_wrong"), "error");
} finally {
state.invite.locked = false;
state.invite.searching = false;
renderInviteModal();
}
}
async function inviteUser(profile) {
const userId = inviteProfileId(profile);
if (state.invite.locked || !userId) return;
const config = inviteConfig();
state.invite.locked = true;
renderInviteModal();
try {
await requestJSON(config.invite, {
method: "POST",
body: { inviteUserId: userId }
});
showToast(message("invitation_submitted"));
state.invite.query = "";
state.invite.showSearch = false;
state.invite.searched = [];
const input = document.querySelector("#inviteSearchInput");
if (input) input.value = "";
await loadInviteRecords({ silent: true });
} catch (error) {
console.error("Invite request failed:", error);
setInviteStatus(error.response?.errorMsg || error.message || message("something_went_wrong"), "error");
} finally {
state.invite.locked = false;
renderInviteModal();
}
}
async function cancelInvite(id) {
if (state.invite.locked || !id) return;
const config = inviteConfig();
state.invite.locked = true;
renderInviteModal();
try {
await requestJSON(config.cancel(id), { method: "POST" });
showToast(message("application_cancelled"));
state.invite.query = "";
state.invite.showSearch = false;
state.invite.searched = [];
const input = document.querySelector("#inviteSearchInput");
if (input) input.value = "";
await loadInviteRecords({ silent: true });
} catch (error) {
console.error("Cancel invite failed:", error);
const text = error.response?.errorMsg?.replace(/^.*\]\s*/, "") || message("cancellation_failed_try_again");
setInviteStatus(text, "error");
} finally {
state.invite.locked = false;
renderInviteModal();
}
}
async function showInviteModal(mode) {
state.invite.mode = normalizeInviteMode(mode);
state.invite.invited = [];
state.invite.searched = [];
state.invite.showSearch = false;
state.invite.query = "";
state.invite.locked = false;
state.invite.searching = false;
setInviteStatus("");
const input = document.querySelector("#inviteSearchInput");
if (input) input.value = "";
renderInviteModal();
setModalOpen("#inviteModal", true);
window.setTimeout(() => document.querySelector("#inviteSearchInput")?.focus(), 80);
await loadInviteRecords();
}
function closeInviteModal() {
setModalOpen("#inviteModal", false);
}
async function fetchProfile() {
state.profile = await requestJSON("/team/member/profile");
renderProfile();
}
async function fetchIdentity() {
state.identity = await requestJSON("/app/h5/identity");
}
async function fetchTeamEntry() {
state.teamEntry = await requestJSON("/team/entry");
}
async function fetchBalanceTotal() {
const salaryTypes = ["BD_SALARY", "BD_LEADER_SALARY"];
const results = await Promise.allSettled(
salaryTypes.map((salaryType) => requestJSON(`/wallet/salary-account/balance?salaryType=${encodeURIComponent(salaryType)}`))
);
const balances = {};
results.forEach((result, index) => {
const salaryType = salaryTypes[index];
if (result.status === "fulfilled") {
balances[salaryType] = result.value || {};
} else {
balances[salaryType] = {};
console.warn(`Failed to load ${salaryType} balance:`, result.reason);
}
});
state.balanceTotal = {
...balances,
availableBalance: salaryTypes.reduce((total, salaryType) => total + balanceAmount(balances[salaryType]), 0)
};
renderBalance();
}
async function fetchBDLeaderBill() {
state.bill = await requestJSON("/team/bd/member-bill/list?type=BDLEADER") || {};
renderBill();
if (normalizeTeamListType(state.teamList.type) === "bd") renderTeamListCard();
}
async function fetchAgencyBill() {
state.agencyBill = await requestJSON("/team/bd/member-bill/list?type=BD") || {};
if (normalizeTeamListType(state.teamList.type) === "agency") renderTeamListCard();
}
async function ensureTeamListData(type) {
const normalizedType = normalizeTeamListType(type);
if (hasTeamListData(teamListBill(normalizedType))) return;
if (normalizedType === "agency") {
await fetchAgencyBill();
return;
}
await fetchBDLeaderBill();
}
async function switchTeamList(type) {
state.teamList.type = normalizeTeamListType(type);
state.teamList.error = "";
state.teamList.loading = !hasTeamListData(teamListBill(state.teamList.type));
renderTeamListCard();
if (!state.teamList.loading) return;
try {
await ensureTeamListData(state.teamList.type);
} catch (error) {
console.error("Failed to load team list:", error);
state.teamList.error = error.response?.errorMsg || error.message || message("failed_to_load");
} finally {
state.teamList.loading = false;
renderTeamListCard();
}
}
async function fetchHistory() {
const history = await requestJSON("/team/bd/leader/history") || {};
const currentHistory = state.bill?.bdHistoryCO;
const details = Array.isArray(history.incomeDetails) ? [...history.incomeDetails] : [];
if (currentHistory) details.unshift(currentHistory);
state.history = { ...history, incomeDetails: details };
renderHistory();
}
async function showHistoryModal() {
try {
setModalOpen("#historyModal", true);
const list = document.querySelector("#historyList");
if (list) {
const loading = document.createElement("div");
loading.className = "empty-state";
loading.textContent = message("loading_data");
replaceChildren(list, [loading]);
}
await fetchHistory();
} catch (error) {
console.error("Failed to load BD leader history:", error);
showToast(error.response?.errorMsg || error.message || message("failed_to_load_history"));
renderHistory();
}
}
async function showHistoryMore(billBelong) {
const path = historyMorePath(billBelong);
if (!path) return;
try {
state.historyMore = await requestJSON(path) || {};
renderDetails();
setModalOpen("#detailsModal", true);
} catch (error) {
console.error("Failed to load BD leader history more:", error);
showToast(error.response?.errorMsg || error.message || message("failed_to_load_history"));
}
}
function buildRouteURL(route) {
const url = new URL(route, window.location.origin);
const token = readRawParam("token");
if (token) url.searchParams.set("token", token);
if (currentLang) url.searchParams.set("lang", currentLang);
return `${url.pathname}${url.search}${url.hash}`;
}
function navigateTo(route) {
window.location.href = buildRouteURL(route);
}
function syncWalletActionLink() {
const nextParams = new URLSearchParams();
const token = readRawParam("token");
if (token) nextParams.set("token", token);
if (currentLang) nextParams.set("lang", currentLang);
nextParams.set("identity", "BD_LEADER");
nextParams.set("salaryType", "BD_LEADER_SALARY");
nextParams.set("mode", "withdraw");
const query = nextParams.toString();
document.querySelectorAll("[data-wallet-action-link]").forEach((link) => {
link.setAttribute("href", `../withdraw-exchange/index.html${query ? `?${query}` : ""}`);
});
}
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 BD leader center page failed:", error);
if (window.history.length > 1) window.history.back();
}
}
function setLanguageMenuOpen(open) {
const button = document.querySelector(".language-button");
const menu = document.querySelector(".language-menu");
if (!button || !menu) return;
menu.hidden = !open;
button.setAttribute("aria-expanded", String(open));
}
function renderLanguageControls() {
const button = document.querySelector(".language-button");
if (button) {
button.textContent = languageLabels[currentLang];
button.setAttribute("aria-label", message("language_button_aria", "Change language"));
}
document.querySelectorAll(".language-menu [data-lang]").forEach((node) => {
const active = node.dataset.lang === currentLang;
node.classList.toggle("is-active", active);
node.setAttribute("aria-pressed", String(active));
});
}
async function applyLanguage(lang) {
currentLang = normalizeLanguage(lang);
currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
document.documentElement.lang = currentLang;
document.documentElement.dir = currentLang === "ar" ? "rtl" : "ltr";
document.title = message("document_title", "BD Leader Center");
renderMessages(currentMessages);
renderLanguageControls();
renderProfile();
renderBalance();
renderBill();
renderHistory();
renderDetails();
renderTeamListCard();
renderInviteModal();
syncWalletActionLink();
}
function clearPayee() {
try {
sessionStorage.removeItem("payee");
localStorage.removeItem("payee");
} catch (error) {
console.debug("payee cache clear skipped:", error);
}
}
function setIdentity() {
try {
localStorage.setItem("identity", JSON.stringify({ identity: "BD_LEADER" }));
} catch (error) {
console.debug("identity cache write skipped:", error);
}
}
function bindEvents() {
document.querySelector(".back-button")?.addEventListener("click", closePage);
document.querySelector(".language-button")?.addEventListener("click", (event) => {
event.stopPropagation();
const menu = document.querySelector(".language-menu");
setLanguageMenuOpen(Boolean(menu?.hidden));
});
document.querySelectorAll(".language-menu [data-lang]").forEach((button) => {
button.addEventListener("click", (event) => {
event.stopPropagation();
setLanguageMenuOpen(false);
applyLanguage(button.dataset.lang);
});
});
document.addEventListener("click", () => setLanguageMenuOpen(false));
document.querySelectorAll("[data-route]").forEach((node) => {
node.addEventListener("click", () => navigateTo(node.dataset.route));
});
document.querySelector("[data-action='history']")?.addEventListener("click", showHistoryModal);
document.querySelectorAll("[data-list-tab]").forEach((button) => {
button.addEventListener("click", () => switchTeamList(button.dataset.listTab));
});
document.querySelector("[data-action='invite-agent']")?.addEventListener("click", () => showInviteModal("agent"));
document.querySelector("[data-action='invite-bd']")?.addEventListener("click", () => showInviteModal("bd"));
document.querySelector("#inviteSearchInput")?.addEventListener("input", handleInviteInput);
document.querySelector("#inviteSearchForm")?.addEventListener("submit", searchInviteUser);
document.querySelectorAll("[data-close-history]").forEach((node) => {
node.addEventListener("click", () => setModalOpen("#historyModal", false));
});
document.querySelectorAll("[data-close-details]").forEach((node) => {
node.addEventListener("click", () => setModalOpen("#detailsModal", false));
});
document.querySelectorAll("[data-close-invite]").forEach((node) => {
node.addEventListener("click", closeInviteModal);
});
document.addEventListener("keydown", (event) => {
if (event.key === "Escape") {
setModalOpen("#historyModal", false);
setModalOpen("#detailsModal", false);
closeInviteModal();
}
});
}
async function initializePage() {
setLoading(true);
clearPayee();
setIdentity();
bindEvents();
syncWalletActionLink();
renderProfile();
renderBalance();
try {
await applyLanguage(currentLang);
const tasks = [
fetchProfile(),
fetchIdentity(),
fetchTeamEntry(),
fetchBalanceTotal(),
fetchBDLeaderBill()
];
const results = await Promise.allSettled(tasks);
const rejected = results.find((item) => item.status === "rejected");
if (rejected) {
console.warn("BD leader center partial load failed:", rejected.reason);
showToast(rejected.reason?.response?.errorMsg || rejected.reason?.message || message("failed_to_load"));
}
} catch (error) {
console.error("Failed to initialize BD leader center:", error);
showToast(error.response?.errorMsg || error.message || message("failed_to_load"));
} finally {
setLoading(false);
}
}
initializePage();
})();