565 lines
19 KiB
JavaScript
565 lines
19 KiB
JavaScript
import { historyMoreUrl } from "../bd-static/shared.js";
|
|
|
|
const API_BASE_URL = "https://jvapi.haiyihy.com";
|
|
|
|
const labels = {
|
|
en: {
|
|
bd_leader_center: "BD Leader Center",
|
|
my_available_salary: "My available salary",
|
|
my_salary_link: "Details",
|
|
bd_policy_link: "BD policy",
|
|
agency_list_link: "Agency list",
|
|
bd_list_link: "BD List",
|
|
invite_become_agent: "Invite to become agent",
|
|
invite_become_BD: "Invite to become BD",
|
|
bank_cards_link: "Bank cards",
|
|
send_welcome_gift: "Send welcome gift",
|
|
bd_salary: "BD salary",
|
|
bd_leader_history: "BD Leader history",
|
|
total_income: "Total income",
|
|
previous_period_income: "Previous period income",
|
|
agency_number: "Agency number",
|
|
bd_number: "BD number",
|
|
team_salary: "Team salary",
|
|
team_top_up: "Team top up",
|
|
bd_income_salary: "BD income salary",
|
|
bd_income_top_up: "BD income top up",
|
|
user_id_prefix: "ID: ",
|
|
salary: "Salary",
|
|
recharge: "Recharge",
|
|
host: "Host",
|
|
agencies: "Agencies",
|
|
total_team_salary: "Total team salary",
|
|
more: "More",
|
|
in_progress: "In Progress",
|
|
pending: "Pending",
|
|
completed: "Completed",
|
|
out_of_account: "Out of account",
|
|
failed_to_load_balance: "Failed to load balance. Please try again.",
|
|
failed_to_load_history: "Failed to load history. Please try again.",
|
|
request_not_available: "Request failed. Please try again."
|
|
},
|
|
ar: {},
|
|
tr: {},
|
|
bn: {}
|
|
};
|
|
|
|
["ar", "tr", "bn"].forEach((lang) => {
|
|
labels[lang] = { ...labels.en };
|
|
});
|
|
|
|
let commonHeaders = {
|
|
authorization: "Bearer undefined",
|
|
"req-app-intel": "version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google",
|
|
"req-client": "Android",
|
|
"req-imei": "8fa54d728ab449e04f9292329ed44bda",
|
|
"req-lang": "id-ID",
|
|
"req-sys-origin": "origin=ATYOU;originChild=ATYOU",
|
|
"req-version": "V2",
|
|
"req-zone": "Asia/Shanghai"
|
|
};
|
|
|
|
const state = {
|
|
lang: resolveLanguage(),
|
|
balanceTotal: {},
|
|
userInfo: null,
|
|
teamInfo: null,
|
|
identityInfo: null,
|
|
identity: "BD_LEADER",
|
|
modalMode: "",
|
|
historyType: "bd",
|
|
bdHistory: {},
|
|
bdLeaderHistory: {},
|
|
historyMore: {}
|
|
};
|
|
|
|
function resolveLanguage() {
|
|
const params = new URLSearchParams(window.location.search);
|
|
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
|
|
const hashParams = new URLSearchParams(hashQuery);
|
|
const lang = params.get("lang") || hashParams.get("lang") || navigator.language || "en";
|
|
if (lang.startsWith("ar")) return "ar";
|
|
if (lang.startsWith("tr")) return "tr";
|
|
if (lang.startsWith("bn")) return "bn";
|
|
return "en";
|
|
}
|
|
|
|
function t(key) {
|
|
return labels[state.lang]?.[key] || labels.en[key] || key;
|
|
}
|
|
|
|
function setLanguage() {
|
|
document.documentElement.lang = state.lang;
|
|
document.documentElement.dir = state.lang === "ar" ? "rtl" : "ltr";
|
|
document.querySelector("[data-action='language']").textContent = state.lang.toUpperCase();
|
|
document.querySelectorAll("[data-i18n]").forEach((node) => {
|
|
node.textContent = t(node.dataset.i18n);
|
|
});
|
|
}
|
|
|
|
function setIcons() {
|
|
const icon = (body) => `data:image/svg+xml;charset=UTF-8,${encodeURIComponent(`
|
|
<svg xmlns="http://www.w3.org/2000/svg" width="48" height="48" viewBox="0 0 48 48" fill="none">
|
|
${body}
|
|
</svg>
|
|
`)}`;
|
|
|
|
const iconMap = {
|
|
policy: icon(`
|
|
<path d="M13 8h15l7 7v25H13V8Z" fill="#E9F8EF" stroke="#22945D" stroke-width="3" stroke-linejoin="round"/>
|
|
<path d="M28 8v8h7" fill="#BDEBD0" stroke="#22945D" stroke-width="3" stroke-linejoin="round"/>
|
|
<path d="M18 32h4l2-9 4 14 2-5h4" stroke="#1766C2" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/>
|
|
`),
|
|
bank: icon(`
|
|
<path d="M8 20h32L24 10 8 20Z" fill="#FFF2C7" stroke="#C08212" stroke-width="3" stroke-linejoin="round"/>
|
|
<path d="M12 22v14M20 22v14M28 22v14M36 22v14" stroke="#2C7A4B" stroke-width="3" stroke-linecap="round"/>
|
|
<path d="M9 38h30" stroke="#C08212" stroke-width="3" stroke-linecap="round"/>
|
|
`),
|
|
gift: icon(`
|
|
<path d="M11 22h26v18H11V22Z" fill="#FFF0B8" stroke="#C47B13" stroke-width="3" stroke-linejoin="round"/>
|
|
<path d="M8 15h32v7H8v-7Z" fill="#FFE4E8" stroke="#D94B63" stroke-width="3" stroke-linejoin="round"/>
|
|
<path d="M24 15v25" stroke="#D94B63" stroke-width="3" stroke-linecap="round"/>
|
|
<path d="M24 15c-7-1-10-3-10-6 0-2 2-4 4-4 4 0 6 6 6 10ZM24 15c7-1 10-3 10-6 0-2-2-4-4-4-4 0-6 6-6 10Z" fill="#FFFFFF" stroke="#D94B63" stroke-width="3" stroke-linejoin="round"/>
|
|
`),
|
|
invite: icon(`
|
|
<circle cx="18" cy="17" r="6" fill="#E8F3FF" stroke="#2878D8" stroke-width="3"/>
|
|
<path d="M7 37c1.5-7 6-11 11-11 2.8 0 5.4 1.2 7.3 3.4" stroke="#2878D8" stroke-width="3" stroke-linecap="round"/>
|
|
<path d="M34 19v14M27 26h14" stroke="#2C9B63" stroke-width="3" stroke-linecap="round"/>
|
|
`),
|
|
arrow: icon(`
|
|
<path d="M19 12l12 12-12 12" stroke="#62A87C" stroke-width="4" stroke-linecap="round" stroke-linejoin="round"/>
|
|
`),
|
|
salary: icon(`
|
|
<circle cx="24" cy="24" r="17" fill="#E8F8EF" stroke="#2C9B63" stroke-width="3"/>
|
|
<path d="M24 13v22" stroke="#2C9B63" stroke-width="3" stroke-linecap="round"/>
|
|
<path d="M30 17.5c-1.4-1.4-3.4-2.2-6-2.2-3.5 0-6 1.8-6 4.7 0 6.7 12 2.8 12 9.1 0 2.7-2.5 4.6-6.2 4.6-2.8 0-5.3-.9-7-2.5" stroke="#D69E17" stroke-width="3" stroke-linecap="round"/>
|
|
`)
|
|
};
|
|
|
|
document.querySelectorAll("[data-icon]").forEach((img) => {
|
|
img.src = iconMap[img.dataset.icon] || "";
|
|
});
|
|
}
|
|
|
|
function splitHeaderPairs(value) {
|
|
const result = {};
|
|
if (!value) return result;
|
|
value.split(";").forEach((item) => {
|
|
const index = item.indexOf("=");
|
|
if (index > -1) result[item.slice(0, index)] = item.slice(index + 1);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
function parseAccessOrigin(raw) {
|
|
const data = JSON.parse(raw);
|
|
const appIntel = splitHeaderPairs(data["Req-App-Intel"]);
|
|
const sysOrigin = splitHeaderPairs(data["Req-Sys-Origin"]);
|
|
return {
|
|
reqLang: data["Req-Lang"],
|
|
authorization: data.Authorization,
|
|
buildVersion: appIntel.build,
|
|
appVersion: appIntel.version,
|
|
appChannel: appIntel.channel,
|
|
reqImei: appIntel["Req-Imei"],
|
|
sysOrigin: sysOrigin.origin,
|
|
sysOriginChild: sysOrigin.child
|
|
};
|
|
}
|
|
|
|
function setHeadersFromApp(headers) {
|
|
const next = {};
|
|
if (headers.authorization) next.authorization = headers.authorization;
|
|
if (headers.reqLang) next["req-lang"] = headers.reqLang;
|
|
if (headers.appVersion || headers.buildVersion || headers.appChannel) {
|
|
const intel = [];
|
|
if (headers.appVersion) intel.push(`version=${headers.appVersion}`);
|
|
if (headers.buildVersion) intel.push(`build=${headers.buildVersion}`);
|
|
if (headers.appChannel) intel.push(`channel=${headers.appChannel}`);
|
|
next["req-app-intel"] = intel.join(";");
|
|
}
|
|
if (headers.reqImei) next["req-imei"] = headers.reqImei;
|
|
if (headers.sysOrigin) {
|
|
const origin = [`origin=${headers.sysOrigin}`];
|
|
if (headers.sysOriginChild) origin.push(`child=${headers.sysOriginChild}`);
|
|
next["req-sys-origin"] = origin.join(";");
|
|
}
|
|
commonHeaders = { ...commonHeaders, ...next };
|
|
}
|
|
|
|
function isMobileDevice() {
|
|
return /Android|iPad|iPhone|iPod/.test(navigator.userAgent);
|
|
}
|
|
|
|
function isAppEnvironment() {
|
|
return Boolean(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp);
|
|
}
|
|
|
|
function requestAccessOrigin() {
|
|
return new Promise((resolve) => {
|
|
window.renderData = resolve;
|
|
window.getIosAccessOriginParam = resolve;
|
|
|
|
if (!isAppEnvironment()) {
|
|
setHeadersFromApp({
|
|
authorization: "",
|
|
reqLang: state.lang,
|
|
appVersion: "1.0.0",
|
|
buildVersion: "1",
|
|
appChannel: "Web",
|
|
reqImei: "H5-STATIC",
|
|
sysOrigin: "LIKEI",
|
|
sysOriginChild: "LIKEI"
|
|
});
|
|
resolve(null);
|
|
return;
|
|
}
|
|
|
|
let attempt = 0;
|
|
const poll = () => {
|
|
attempt += 1;
|
|
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
|
resolve(window.app.getAccessOrigin());
|
|
return;
|
|
}
|
|
if (attempt < 50) {
|
|
window.setTimeout(poll, 100);
|
|
return;
|
|
}
|
|
if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") {
|
|
window.FlutterApp.postMessage("requestAccessOrigin");
|
|
}
|
|
};
|
|
|
|
if (isMobileDevice() || isAppEnvironment()) {
|
|
poll();
|
|
} else {
|
|
resolve(null);
|
|
}
|
|
});
|
|
}
|
|
|
|
async function connectToApp() {
|
|
const raw = await requestAccessOrigin();
|
|
if (!raw) return { success: true, environment: "browser" };
|
|
setHeadersFromApp(parseAccessOrigin(raw));
|
|
return { success: true, environment: "app" };
|
|
}
|
|
|
|
async function apiGet(path) {
|
|
const response = await fetch(`${API_BASE_URL}${path}`, {
|
|
method: "GET",
|
|
headers: commonHeaders
|
|
});
|
|
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;
|
|
}
|
|
|
|
async function fetchUserProfile() {
|
|
const result = await apiGet("/team/member/profile");
|
|
if (result.status && result.body) state.userInfo = result.body;
|
|
}
|
|
|
|
async function fetchTeamInfo() {
|
|
try {
|
|
const result = await apiGet("/team/entry");
|
|
if (result.status && result.body) state.teamInfo = result.body.teamProfile || result.body;
|
|
} catch (error) {
|
|
state.teamInfo = null;
|
|
console.debug("BD leader team info unavailable:", error);
|
|
}
|
|
}
|
|
|
|
async function fetchIdentity() {
|
|
const result = await apiGet("/app/h5/identity");
|
|
if (result.status && result.body) state.identityInfo = result.body;
|
|
}
|
|
|
|
async function fetchBalanceTotal() {
|
|
const result = await apiGet("/wallet/salary-account/balance/total");
|
|
if (result.status && result.body) state.balanceTotal = result.body || {};
|
|
}
|
|
|
|
async function fetchBDHistoryMore(billBelong) {
|
|
const endpoint = historyMoreUrl("/team/bd/history/more", billBelong);
|
|
if (!endpoint) throw new Error(t("request_not_available"));
|
|
const result = await apiGet(endpoint);
|
|
if (result.status && result.body) state.historyMore = result.body || {};
|
|
}
|
|
|
|
async function fetchBDLeaderHistoryMore(billBelong) {
|
|
const endpoint = historyMoreUrl("/team/bd/leader/history/more", billBelong);
|
|
if (!endpoint) throw new Error(t("request_not_available"));
|
|
const result = await apiGet(endpoint);
|
|
if (result.status && result.body) state.historyMore = result.body || {};
|
|
}
|
|
|
|
function setIdentity(identity) {
|
|
state.identity = identity;
|
|
localStorage.setItem("identity", JSON.stringify({ identity }));
|
|
}
|
|
|
|
function clearPayee() {
|
|
try {
|
|
sessionStorage.removeItem("payee");
|
|
localStorage.removeItem("payee");
|
|
} catch (error) {
|
|
console.debug("Payee cache clear skipped:", error);
|
|
}
|
|
}
|
|
|
|
function formatMoney(value) {
|
|
if (value === null || value === undefined || value === "") return "0";
|
|
const number = Number(value);
|
|
if (Number.isNaN(number)) return String(value);
|
|
return Number.isInteger(number) ? String(number) : number.toFixed(2);
|
|
}
|
|
|
|
function normalizeStatus(status) {
|
|
if (status === "UNPAID" || status === "In Progress" || status === t("in_progress")) return t("in_progress");
|
|
if (status === "Pending" || status === t("pending")) return t("pending");
|
|
if (status === "SETTLED" || status === "PAY_OUT" || status === "Completed" || status === t("completed")) return t("completed");
|
|
if (status === "HANG_UP" || status === "Out of account" || status === "Out Of Account" || status === t("out_of_account")) return t("out_of_account");
|
|
return status || "";
|
|
}
|
|
|
|
function statusClass(status) {
|
|
const value = normalizeStatus(status);
|
|
if (value === t("completed")) return "is-completed";
|
|
if (value === t("pending")) return "is-pending";
|
|
if (value === t("out_of_account")) return "is-out";
|
|
return "is-progress";
|
|
}
|
|
|
|
function navigateTo(target) {
|
|
const url = new URL(target, window.location.origin);
|
|
const currentLang = new URLSearchParams(window.location.search).get("lang");
|
|
if (currentLang && !url.searchParams.has("lang")) url.searchParams.set("lang", currentLang);
|
|
window.location.href = `${url.pathname}${url.search}${url.hash}`;
|
|
}
|
|
|
|
function closePage() {
|
|
if (window.history.length > 1) {
|
|
window.history.back();
|
|
return;
|
|
}
|
|
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");
|
|
}
|
|
}
|
|
|
|
function renderBalance() {
|
|
document.getElementById("availableBalance").textContent = `$${formatMoney(state.balanceTotal.availableBalance || 0)}`;
|
|
}
|
|
|
|
function historySource() {
|
|
return state.historyType === "bd" ? state.bdHistory : state.bdLeaderHistory;
|
|
}
|
|
|
|
function historyTitle() {
|
|
return state.historyType === "bd" ? t("bd_salary") : t("bd_leader_history");
|
|
}
|
|
|
|
function renderHistorySheet() {
|
|
const data = historySource();
|
|
document.getElementById("historyTitle").textContent = historyTitle();
|
|
document.getElementById("historyTotalIncome").textContent = formatMoney(data.totalIncome || 0);
|
|
document.getElementById("historyPreviousIncome").textContent = formatMoney(data.previousPeriodIncome || 0);
|
|
|
|
const list = document.getElementById("historyList");
|
|
list.innerHTML = "";
|
|
const items = data.incomeDetails || [];
|
|
items.forEach((item) => list.appendChild(createHistoryItem(item)));
|
|
}
|
|
|
|
function createHistoryItem(item) {
|
|
const card = document.createElement("article");
|
|
card.className = `salary-item ${statusClass(item.statusText)}`;
|
|
|
|
const status = document.createElement("div");
|
|
status.className = "status-background";
|
|
status.textContent = normalizeStatus(item.statusText);
|
|
card.appendChild(status);
|
|
|
|
const content = document.createElement("div");
|
|
content.className = "salary-item-content";
|
|
if (state.historyType === "bd") {
|
|
content.append(
|
|
textLine(item.billTitle || ""),
|
|
textLine(`${t("agency_number")}: ${item.agencyNumber || 0}`),
|
|
pairLine(`${t("team_salary")}: $${item.teamSalaryAmount || 0}`, `${t("bd_income_salary")}: $${item.bdIncomeSalary || item.settlementSalary || 0} (${item.bdRatioSalary || item.commissionRate || 0}%)`),
|
|
pairLine(`${t("team_top_up")}: $${item.teamRechargeAmount || 0}`, `${t("bd_income_top_up")}: $${item.bdIncomeRecharge || item.settlementSalary || 0} (${item.bdRatioRecharge || item.commissionRate || 0}%)`)
|
|
);
|
|
} else {
|
|
content.append(
|
|
textLine(item.billTitle || ""),
|
|
pairLine(`${t("bd_number")}: ${item.bdNumber || 0}`, `${t("team_salary")}: $${item.teamSalaryAmount || 0}`)
|
|
);
|
|
}
|
|
card.appendChild(content);
|
|
|
|
if (normalizeStatus(item.statusText) === t("completed")) {
|
|
const more = document.createElement("button");
|
|
more.type = "button";
|
|
more.className = "more-bt";
|
|
more.textContent = t("more");
|
|
more.addEventListener("click", () => showHistoryMore(item.billBelong));
|
|
card.appendChild(more);
|
|
}
|
|
return card;
|
|
}
|
|
|
|
function textLine(text) {
|
|
const node = document.createElement("div");
|
|
node.textContent = text;
|
|
return node;
|
|
}
|
|
|
|
function pairLine(left, right) {
|
|
const node = document.createElement("div");
|
|
node.className = "pair-line";
|
|
const leftNode = document.createElement("div");
|
|
const rightNode = document.createElement("div");
|
|
leftNode.textContent = left;
|
|
rightNode.textContent = right;
|
|
node.append(leftNode, rightNode);
|
|
return node;
|
|
}
|
|
|
|
async function showHistoryMore(billBelong) {
|
|
try {
|
|
if (state.historyType === "bd") {
|
|
await fetchBDHistoryMore(billBelong);
|
|
} else {
|
|
await fetchBDLeaderHistoryMore(billBelong);
|
|
}
|
|
state.modalMode = "details";
|
|
renderModal();
|
|
} catch (error) {
|
|
console.warn("BD leader history more failed:", error);
|
|
showToast(error.response?.errorMsg || error.message || t("failed_to_load_history"));
|
|
}
|
|
}
|
|
|
|
function renderDetailsSheet() {
|
|
const data = state.historyMore || {};
|
|
document.getElementById("detailsPeriod").textContent = data.period || "";
|
|
const summary = document.getElementById("detailsSummary");
|
|
summary.innerHTML = "";
|
|
if (state.historyType === "bd") {
|
|
summary.append(
|
|
textLine(`${t("team_top_up")}: $${data.teamTotalRecharge || 0}`),
|
|
textLine(`${t("total_team_salary")}: $${data.teamTotalSalary || 0}`)
|
|
);
|
|
} else {
|
|
summary.append(
|
|
textLine(`${t("team_salary")}: $${data.teamSalary || 0}`),
|
|
textLine(`${t("bd_number")}: ${data.bdNumber || 0}`)
|
|
);
|
|
}
|
|
|
|
const list = document.getElementById("memberDetails");
|
|
list.innerHTML = "";
|
|
(data.memberDetails || []).forEach((item) => {
|
|
const card = document.createElement("article");
|
|
card.className = "member-item";
|
|
const img = document.createElement("img");
|
|
img.src = state.historyType === "bd" ? item.userAvatar || "" : item.bdUserAvatar || "";
|
|
img.alt = "";
|
|
img.addEventListener("error", () => {
|
|
img.removeAttribute("src");
|
|
});
|
|
const info = document.createElement("div");
|
|
info.className = "member-info";
|
|
info.append(
|
|
textLine(state.historyType === "bd" ? item.userName || "" : item.bdUserName || ""),
|
|
textLine(`${t("user_id_prefix")}${item.account || ""}`)
|
|
);
|
|
const metrics = document.createElement("div");
|
|
metrics.className = "member-metrics";
|
|
metrics.append(textLine(`${t("salary")}: $${item.salary || 0}`));
|
|
if (state.historyType === "bd") {
|
|
metrics.append(textLine(`${t("recharge")}: $${item.recharge || 0}`), textLine(`${t("host")}: ${item.hostCount || 0}`));
|
|
} else {
|
|
metrics.append(textLine(`${t("agencies")}: ${item.agencyCount || 0}`));
|
|
}
|
|
card.append(img, info, metrics);
|
|
list.appendChild(card);
|
|
});
|
|
}
|
|
|
|
function renderModal() {
|
|
const mask = document.getElementById("maskLayer");
|
|
const history = document.getElementById("historySheet");
|
|
const details = document.getElementById("detailsSheet");
|
|
const showHistory = state.modalMode === "history";
|
|
const showDetails = state.modalMode === "details";
|
|
mask.hidden = !showHistory && !showDetails;
|
|
history.hidden = !showHistory;
|
|
details.hidden = !showDetails;
|
|
if (showHistory) renderHistorySheet();
|
|
if (showDetails) renderDetailsSheet();
|
|
}
|
|
|
|
function closeModal() {
|
|
state.modalMode = "";
|
|
state.historyMore = {};
|
|
renderModal();
|
|
}
|
|
|
|
function showToast(message) {
|
|
const toast = document.getElementById("toast");
|
|
toast.textContent = message;
|
|
toast.hidden = false;
|
|
window.clearTimeout(showToast.timer);
|
|
showToast.timer = window.setTimeout(() => {
|
|
toast.hidden = true;
|
|
}, 2500);
|
|
}
|
|
|
|
function bindEvents() {
|
|
document.querySelector("[data-action='back']").addEventListener("click", closePage);
|
|
document.querySelector("[data-action='language']").addEventListener("click", () => navigateTo("/language"));
|
|
document.querySelectorAll("[data-route]").forEach((node) => {
|
|
node.addEventListener("click", () => navigateTo(node.dataset.route));
|
|
});
|
|
document.querySelector("[data-action='sendGift']").addEventListener("click", () => navigateTo("/bd-item-distribution"));
|
|
document.querySelector("[data-action='closeModal']").addEventListener("click", closeModal);
|
|
}
|
|
|
|
async function initializePageData() {
|
|
const tasks = [fetchUserProfile(), fetchTeamInfo(), fetchIdentity()];
|
|
await Promise.allSettled(tasks);
|
|
}
|
|
|
|
async function init() {
|
|
setLanguage();
|
|
setIcons();
|
|
bindEvents();
|
|
renderBalance();
|
|
clearPayee();
|
|
setIdentity("BD_LEADER");
|
|
|
|
try {
|
|
await connectToApp();
|
|
await initializePageData();
|
|
await fetchBalanceTotal();
|
|
} catch (error) {
|
|
console.warn("BD leader center initialization failed:", error);
|
|
showToast(error.response?.errorMsg || error.message || t("failed_to_load_balance"));
|
|
} finally {
|
|
renderBalance();
|
|
}
|
|
}
|
|
|
|
init();
|