(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: "My available salary", details: "Details", bd_number: "BD number", team_salary: "Team salary", bd_policy_link: "BD policy", bd_leader_history: "BD Leader history", agency_list_link: "Agency list", bd_list_link: "BD List", invite_become_agent: "Invite to become agent", invite_become_bd: "Invite to become BD", 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", loading_data: "Loading...", no_data: "No data", in_progress: "In Progress", pending: "Pending", completed: "Completed", out_of_account: "Out of account", 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_policy_link: "سياسة BD", bd_leader_history: "سجل قائد BD", agency_list_link: "قائمة الوكالات", bd_list_link: "قائمة BD", invite_become_agent: "دعوة ليصبح وكيلا", invite_become_bd: "دعوة ليصبح BD", 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: "خارج الحساب", 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: "Kullanılabilir maaşım", details: "Detaylar", bd_number: "BD sayısı", team_salary: "Takım maaşı", bd_policy_link: "BD politikası", bd_leader_history: "BD Leader geçmişi", agency_list_link: "Ajans listesi", bd_list_link: "BD Listesi", invite_become_agent: "Ajans olmaya davet et", invite_become_bd: "BD olmaya davet et", 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ışı", 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 tersedia", details: "Detail", bd_number: "Jumlah BD", team_salary: "Gaji tim", bd_policy_link: "Kebijakan BD", bd_leader_history: "Riwayat BD Leader", agency_list_link: "Daftar agensi", bd_list_link: "Daftar BD", invite_become_agent: "Undang menjadi agen", invite_become_bd: "Undang menjadi BD", 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", 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: {}, history: null, historyMore: null }; 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); }); } 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) { const token = readRawParam("token"); if (!token) throw new Error("Missing token"); const response = await fetch(new URL(path, `${apiBaseURL()}/`).toString(), { cache: "no-store", headers: authHeadersFromToken(token) }); 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")).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 renderBill() { const bill = state.bill || {}; const members = Array.isArray(bill.memberBillList) ? bill.memberBillList : []; const billTitle = document.querySelector("#billTitle"); const bdNumber = document.querySelector("#bdNumber"); const teamSalary = document.querySelector("#teamSalary"); if (billTitle) billTitle.textContent = bill.billTitle || "-"; 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) { 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 salary = document.createElement("div"); salary.textContent = `${message("team_salary")}: $${formatMoney(item?.teamSalaryAmount || 0)}`; const agencies = document.createElement("div"); agencies.textContent = `${message("agencies")}: ${formatCount(item?.teamMemberCount || item?.agencyCount || 0)}`; side.append(salary, agencies); 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 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?.billBelong)); 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)); } function normalizeBillBelong(value) { const text = String(value || "").trim(); if (!text) return ""; const iso = text.match(/^(\d{4})-(\d{2})-(\d{2})(?:[ T](\d{2}):(\d{2})(?::(\d{2}))?)?/); if (iso) return `${iso[1]}-${iso[2]}-${iso[3]}T${iso[4] || "00"}:${iso[5] || "00"}:${iso[6] || "00"}`; const compact = text.match(/^(\d{4})(\d{2})(\d{2})(?:(\d{2})(\d{2})(\d{2})?)?$/); if (compact) return `${compact[1]}-${compact[2]}-${compact[3]}T${compact[4] || "00"}:${compact[5] || "00"}:${compact[6] || "00"}`; return text; } function historyMorePath(billBelong) { const normalized = normalizeBillBelong(billBelong); 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; })); } 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() { state.balanceTotal = await requestJSON("/wallet/salary-account/balance/total") || {}; renderBalance(); } async function fetchBDLeaderBill() { state.bill = await requestJSON("/team/bd/member-bill/list?type=BDLEADER") || {}; renderBill(); } 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 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(); } 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-close-history]").forEach((node) => { node.addEventListener("click", () => setModalOpen("#historyModal", false)); }); document.querySelectorAll("[data-close-details]").forEach((node) => { node.addEventListener("click", () => setModalOpen("#detailsModal", false)); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") { setModalOpen("#historyModal", false); setModalOpen("#detailsModal", false); } }); } async function initializePage() { setLoading(true); clearPayee(); setIdentity(); bindEvents(); 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(); })();