diff --git a/h5/hyapp/bd-center/index.html b/h5/hyapp/bd-center/index.html
new file mode 100644
index 0000000..e81ae30
--- /dev/null
+++ b/h5/hyapp/bd-center/index.html
@@ -0,0 +1,131 @@
+
+
+
+
+
+ BD Center
+
+
+
+
+
+
+
+
+
+
+
+
+
+
![]()
+
B
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Invite Agent
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
loading....
+
+
+
+
+
+
+
diff --git a/h5/hyapp/bd-center/script.js b/h5/hyapp/bd-center/script.js
new file mode 100644
index 0000000..196a76e
--- /dev/null
+++ b/h5/hyapp/bd-center/script.js
@@ -0,0 +1,1114 @@
+(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 Center",
+ page_label: "BD Center",
+ back: "Back",
+ title: "BD Center",
+ language_button_aria: "Change language",
+ role: "BD",
+ uid_prefix: "UID:",
+ my_available_salary: "Salary",
+ details: "Withdraw/Exchange",
+ bd_number: "BD number",
+ team_salary: "Team salary",
+ agency_list_link: "Agency List",
+ invite_become_agent: "Invite Agent",
+ invite_agent_title: "Invite Agent",
+ enter_user_id: "Enter User ID",
+ search: "Search",
+ searching: "Searching...",
+ invite: "Invite",
+ inviting: "Inviting...",
+ cancel: "Cancel",
+ success: "Success",
+ 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"
+ },
+ 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: "راتب الفريق",
+ agency_list_link: "قائمة الوكالات",
+ invite_become_agent: "دعوة وكيل",
+ invite_agent_title: "دعوة وكيل",
+ enter_user_id: "أدخل معرف المستخدم",
+ search: "بحث",
+ searching: "جار البحث...",
+ invite: "دعوة",
+ inviting: "جار الدعوة...",
+ cancel: "إلغاء",
+ success: "نجاح",
+ 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: "فشل تحميل البيانات"
+ },
+ tr: {
+ document_title: "BD Merkezi",
+ page_label: "BD Merkezi",
+ back: "Geri",
+ title: "BD Merkezi",
+ language_button_aria: "Dili değiştir",
+ role: "BD",
+ uid_prefix: "UID:",
+ my_available_salary: "Maaş",
+ details: "Withdraw/Exchange",
+ bd_number: "BD sayısı",
+ team_salary: "Takım maaşı",
+ agency_list_link: "Ajans listesi",
+ invite_become_agent: "Ajans Davet Et",
+ invite_agent_title: "Ajans 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ı",
+ 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"
+ },
+ id: {
+ document_title: "Pusat BD",
+ page_label: "Pusat BD",
+ back: "Kembali",
+ title: "Pusat BD",
+ language_button_aria: "Ubah bahasa",
+ role: "BD",
+ uid_prefix: "UID:",
+ my_available_salary: "Gaji",
+ details: "Withdraw/Exchange",
+ bd_number: "Jumlah BD",
+ team_salary: "Gaji tim",
+ agency_list_link: "Daftar agensi",
+ invite_become_agent: "Invite Agent",
+ invite_agent_title: "Invite Agent",
+ enter_user_id: "Masukkan ID Pengguna",
+ search: "Cari",
+ searching: "Mencari...",
+ invite: "Undang",
+ inviting: "Mengundang...",
+ cancel: "Batal",
+ success: "Berhasil",
+ 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"
+ }
+ };
+
+ 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,
+ teamList: {
+ type: "agency",
+ 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-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(".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.agencyBill || 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 "agency";
+ }
+
+ function teamListBill() {
+ return state.agencyBill || state.bill;
+ }
+
+ function hasTeamListData(data) {
+ return Boolean(data && (Array.isArray(data.memberBillList) || 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 inviteConfig(mode = state.invite.mode) {
+ 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 "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() {
+ state.balanceTotal = await requestJSON("/wallet/salary-account/balance?salaryType=BD_SALARY") || {};
+ renderBalance();
+ }
+
+ async function fetchAgencyBill() {
+ state.agencyBill = await requestJSON("/team/bd/member-bill/list?type=BD") || {};
+ renderBill();
+ renderTeamListCard();
+ }
+
+ async function ensureTeamListData(type) {
+ const normalizedType = normalizeTeamListType(type);
+ if (hasTeamListData(teamListBill(normalizedType))) return;
+ await fetchAgencyBill();
+ }
+
+ 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();
+ }
+ }
+
+ 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 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 Center");
+ renderMessages(currentMessages);
+ renderLanguageControls();
+ renderProfile();
+ renderBalance();
+ renderBill();
+ renderTeamListCard();
+ renderInviteModal();
+ }
+
+ 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" }));
+ } 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.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("#inviteSearchInput")?.addEventListener("input", handleInviteInput);
+ document.querySelector("#inviteSearchForm")?.addEventListener("submit", searchInviteUser);
+ document.querySelectorAll("[data-close-invite]").forEach((node) => {
+ node.addEventListener("click", closeInviteModal);
+ });
+ document.addEventListener("keydown", (event) => {
+ if (event.key === "Escape") {
+ closeInviteModal();
+ }
+ });
+ }
+
+ async function initializePage() {
+ setLoading(true);
+ clearPayee();
+ setIdentity();
+ bindEvents();
+ renderProfile();
+ renderBalance();
+
+ try {
+ await applyLanguage(currentLang);
+ const tasks = [
+ fetchProfile(),
+ fetchIdentity(),
+ fetchTeamEntry(),
+ fetchBalanceTotal(),
+ fetchAgencyBill()
+ ];
+ const results = await Promise.allSettled(tasks);
+ const rejected = results.find((item) => item.status === "rejected");
+ if (rejected) {
+ console.warn("BD 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 center:", error);
+ showToast(error.response?.errorMsg || error.message || message("failed_to_load"));
+ } finally {
+ setLoading(false);
+ }
+ }
+
+ initializePage();
+})();
diff --git a/h5/hyapp/bd-center/style.css b/h5/hyapp/bd-center/style.css
new file mode 100644
index 0000000..776c64c
--- /dev/null
+++ b/h5/hyapp/bd-center/style.css
@@ -0,0 +1,1269 @@
+:root {
+ --page-bg: #f6f7f7;
+ --card-bg: rgba(255, 255, 255, 0.96);
+ --text: #25282e;
+ --muted: #8f9196;
+ --line: #eef0f2;
+ --mint: #43e7d8;
+ --green: #25d878;
+ --shadow: 0 8px 22px rgba(36, 46, 60, 0.055);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ width: 100%;
+ min-height: 100%;
+ margin: 0;
+ -webkit-font-smoothing: antialiased;
+ -webkit-text-size-adjust: 100%;
+ -webkit-tap-highlight-color: transparent;
+}
+
+body {
+ display: flex;
+ justify-content: center;
+ background: #111;
+ color: var(--text);
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+}
+
+button {
+ border: 0;
+ padding: 0;
+ background: transparent;
+ color: inherit;
+ font: inherit;
+}
+
+[hidden] {
+ display: none !important;
+}
+
+.bd-center {
+ position: relative;
+ width: 100%;
+ max-width: 430px;
+ min-height: 100vh;
+ overflow: hidden;
+ background: var(--page-bg);
+}
+
+.bd-center[data-loading="true"] .title-bar,
+.bd-center[data-loading="true"] .content,
+.bd-center[data-loading="true"] .home-indicator {
+ visibility: hidden;
+}
+
+.hero-bg {
+ position: absolute;
+ inset: 0 0 auto;
+ height: 215px;
+ background:
+ radial-gradient(circle at 72% 4%, rgba(209, 255, 248, 0.98) 0, rgba(209, 255, 248, 0.88) 23%, rgba(209, 255, 248, 0) 48%),
+ radial-gradient(circle at 14% 20%, rgba(185, 250, 244, 0.78) 0, rgba(185, 250, 244, 0) 35%),
+ linear-gradient(151deg, #ccf7ef 0%, #ebfffa 58%, rgba(242, 255, 252, 0.7) 100%);
+ clip-path: polygon(0 0, 100% 0, 100% 76%, 70% 47%, 40% 58%, 0 78%);
+}
+
+.status-bar,
+.title-bar,
+.content,
+.home-indicator {
+ position: relative;
+ z-index: 1;
+}
+
+.status-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ height: 47px;
+ padding: 18px 29px 0 38px;
+ color: #11161c;
+ direction: ltr;
+ font-size: 15px;
+ font-weight: 800;
+}
+
+.status-icons {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+}
+
+.cellular {
+ display: flex;
+ align-items: flex-end;
+ gap: 2px;
+ width: 18px;
+ height: 12px;
+}
+
+.cellular i {
+ width: 3px;
+ border-radius: 2px;
+ background: #11161c;
+}
+
+.cellular i:nth-child(1) { height: 4px; }
+.cellular i:nth-child(2) { height: 6px; }
+.cellular i:nth-child(3) { height: 9px; }
+.cellular i:nth-child(4) { height: 12px; }
+
+.wifi {
+ position: relative;
+ display: inline-flex;
+ width: 16px;
+ height: 12px;
+}
+
+.wifi i {
+ position: absolute;
+ left: 50%;
+ bottom: 0;
+ border: 2px solid transparent;
+ border-top-color: #11161c;
+ border-radius: 50%;
+ transform: translateX(-50%);
+}
+
+.wifi i:nth-child(1) { width: 16px; height: 16px; }
+.wifi i:nth-child(2) { width: 11px; height: 11px; bottom: 2px; }
+.wifi i:nth-child(3) { width: 5px; height: 5px; bottom: 4px; background: #11161c; border: 0; }
+
+.battery {
+ position: relative;
+ width: 24px;
+ height: 12px;
+ border: 2px solid #11161c;
+ border-radius: 2px;
+}
+
+.battery::after {
+ content: "";
+ position: absolute;
+ right: -4px;
+ top: 3px;
+ width: 2px;
+ height: 4px;
+ border-radius: 0 2px 2px 0;
+ background: #11161c;
+}
+
+.battery i {
+ display: block;
+ width: 16px;
+ height: 100%;
+ background: #11161c;
+}
+
+.title-bar {
+ display: grid;
+ grid-template-columns: 44px minmax(0, 1fr) 56px;
+ align-items: center;
+ z-index: 12;
+ min-height: 56px;
+ padding: 8px 14px 6px;
+}
+
+.back-button,
+.language-button {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+ color: #2c3036;
+}
+
+.back-button svg {
+ width: 30px;
+ height: 30px;
+ fill: none;
+ stroke: currentColor;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 2.4;
+}
+
+.title-bar h1 {
+ min-width: 0;
+ margin: 0;
+ overflow: hidden;
+ color: #24282e;
+ font-size: 19px;
+ font-weight: 900;
+ line-height: 1.2;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.language-switcher {
+ position: relative;
+ display: flex;
+ justify-content: flex-end;
+}
+
+.language-button {
+ width: auto;
+ min-width: 42px;
+ height: 32px;
+ padding: 0 10px;
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.55);
+ font-size: 12px;
+ font-weight: 900;
+}
+
+.language-menu {
+ position: absolute;
+ top: 38px;
+ right: 0;
+ z-index: 30;
+ display: grid;
+ gap: 4px;
+ min-width: 74px;
+ padding: 8px;
+ border: 1px solid rgba(34, 196, 180, 0.18);
+ border-radius: 8px;
+ background: #fff;
+ box-shadow: 0 12px 24px rgba(22, 31, 42, 0.12);
+}
+
+.language-menu button {
+ min-height: 30px;
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 850;
+}
+
+.language-menu button.is-active {
+ background: #e7fbf8;
+ color: #0aa397;
+}
+
+.content {
+ display: grid;
+ gap: 12px;
+ padding: 8px 14px 32px;
+}
+
+.card {
+ border-radius: 8px;
+ background: var(--card-bg);
+ box-shadow: var(--shadow);
+}
+
+.profile-card {
+ display: flex;
+ align-items: center;
+ min-height: 104px;
+ padding: 16px;
+}
+
+.avatar-shell,
+.member-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ border-radius: 50%;
+ background: linear-gradient(145deg, #d9fff5, #43e7d8);
+}
+
+.avatar-shell {
+ width: 70px;
+ height: 70px;
+ flex: 0 0 70px;
+}
+
+.avatar-image,
+.member-avatar img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.avatar-fallback,
+.member-fallback {
+ color: #0b5f56;
+ font-size: 26px;
+ font-weight: 950;
+}
+
+.profile-copy {
+ min-width: 0;
+ flex: 1;
+ margin-left: 16px;
+}
+
+.role-pill {
+ display: inline-flex;
+ align-items: center;
+ min-height: 22px;
+ padding: 0 9px;
+ border-radius: 999px;
+ background: #e7fbf8;
+ color: #0b958a;
+ font-size: 11px;
+ font-weight: 900;
+}
+
+.name {
+ margin-top: 7px;
+ overflow: hidden;
+ color: #25282e;
+ font-size: 20px;
+ font-weight: 900;
+ line-height: 1.15;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.meta {
+ margin-top: 6px;
+ overflow: hidden;
+ color: var(--muted);
+ font-size: 13px;
+ font-weight: 750;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.salary-card {
+ min-height: 113px;
+ padding: 18px 20px;
+}
+
+.salary-head,
+.section-head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.salary-label,
+.section-title {
+ color: #24282e;
+ font-size: 16px;
+ font-weight: 900;
+ line-height: 1.2;
+}
+
+.section-subtitle {
+ margin-top: 6px;
+ color: #93959a;
+ font-size: 12px;
+ font-weight: 800;
+ line-height: 1.2;
+}
+
+.text-link {
+ flex: 0 0 auto;
+ color: #10bcb0;
+ font-size: 13px;
+ font-weight: 900;
+}
+
+.salary-value {
+ display: flex;
+ align-items: baseline;
+ gap: 6px;
+ margin-top: 16px;
+ color: #1f242b;
+}
+
+.salary-value span,
+.metric-money span {
+ color: #21b66a;
+ font-weight: 950;
+}
+
+.salary-value strong {
+ min-width: 0;
+ overflow-wrap: anywhere;
+ font-size: 31px;
+ font-weight: 950;
+ line-height: 1.08;
+}
+
+.menu-card {
+ overflow: hidden;
+}
+
+.menu-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 14px;
+ width: 100%;
+ min-height: 58px;
+ padding: 0 16px;
+ border-bottom: 1px solid var(--line);
+}
+
+.menu-row:last-child {
+ border-bottom: 0;
+}
+
+.menu-left {
+ display: flex;
+ align-items: center;
+ min-width: 0;
+ gap: 14px;
+}
+
+.menu-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 30px;
+ height: 30px;
+ flex: 0 0 30px;
+ border-radius: 8px;
+ background: #e9fbf4;
+ color: #1f9d66;
+}
+
+.menu-icon svg {
+ width: 22px;
+ height: 22px;
+ fill: none;
+ stroke: currentColor;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 2.1;
+}
+
+.menu-left span:last-child {
+ min-width: 0;
+ overflow: hidden;
+ color: #464951;
+ font-size: 15px;
+ font-weight: 850;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.chevron {
+ color: #8b8d92;
+ font-size: 24px;
+ line-height: 1;
+}
+
+.team-list-card {
+ display: grid;
+ gap: 12px;
+ padding: 14px;
+}
+
+.team-list-tabs {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+ padding: 4px;
+ border-radius: 8px;
+ background: #f2f6f6;
+}
+
+.team-list-tabs.is-single {
+ grid-template-columns: minmax(0, 1fr);
+}
+
+.team-list-tab {
+ min-width: 0;
+ min-height: 38px;
+ padding: 0 10px;
+ border-radius: 7px;
+ color: #697078;
+ font-size: 13px;
+ font-weight: 950;
+ line-height: 1.1;
+}
+
+.team-list-tab.is-active {
+ background: linear-gradient(135deg, #30dfb4, #15bda9);
+ color: #fff;
+ box-shadow: 0 8px 18px rgba(21, 189, 169, 0.24);
+}
+
+.team-list-summary {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(96px, 1fr));
+ gap: 8px;
+}
+
+.team-list-summary > div {
+ min-width: 0;
+ padding: 10px;
+ border-radius: 8px;
+ background: #f7fbfa;
+}
+
+.team-list-summary span {
+ display: block;
+ overflow: hidden;
+ color: #8b8d92;
+ font-size: 11px;
+ font-weight: 850;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.team-list-summary strong {
+ display: block;
+ margin-top: 4px;
+ overflow-wrap: anywhere;
+ color: #24282e;
+ font-size: 15px;
+ font-weight: 950;
+ line-height: 1.15;
+}
+
+.team-list-content {
+ display: grid;
+ gap: 10px;
+ max-height: 360px;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.member-card {
+ padding: 16px;
+}
+
+.member-list {
+ display: grid;
+ gap: 10px;
+ margin-top: 14px;
+}
+
+.member-row,
+.history-row,
+.details-row {
+ display: grid;
+ gap: 10px;
+ padding: 12px;
+ border: 1px solid #eef0f2;
+ border-radius: 8px;
+ background: #fbfcfc;
+}
+
+.member-row {
+ grid-template-columns: 44px minmax(0, 1fr) auto;
+ align-items: center;
+}
+
+.member-avatar {
+ width: 44px;
+ height: 44px;
+}
+
+.member-fallback {
+ font-size: 18px;
+}
+
+.member-main {
+ min-width: 0;
+}
+
+.member-name {
+ overflow: hidden;
+ color: #25282e;
+ font-size: 15px;
+ font-weight: 900;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.member-meta {
+ margin-top: 4px;
+ overflow: hidden;
+ color: #8b8d92;
+ font-size: 12px;
+ font-weight: 800;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.member-side {
+ display: grid;
+ gap: 3px;
+ color: #555b64;
+ font-size: 12px;
+ font-weight: 850;
+ line-height: 1.2;
+ text-align: right;
+}
+
+.empty-state {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 80px;
+ border: 1px dashed #e5e8ea;
+ border-radius: 8px;
+ color: #8b8d92;
+ font-size: 14px;
+ font-weight: 850;
+ text-align: center;
+}
+
+.history-modal[hidden],
+.details-modal[hidden],
+.invite-modal[hidden] {
+ display: none;
+}
+
+.history-modal,
+.details-modal,
+.invite-modal {
+ position: fixed;
+ z-index: 24;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 18px;
+}
+
+.history-backdrop,
+.invite-backdrop {
+ position: absolute;
+ inset: 0;
+ background: rgba(15, 21, 28, 0.38);
+}
+
+.history-dialog,
+.invite-dialog {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ width: min(394px, 100%);
+ max-height: 78vh;
+ max-height: 78dvh;
+ overflow: hidden;
+ border-radius: 14px;
+ background: #fff;
+ box-shadow: 0 18px 44px rgba(23, 31, 42, 0.2);
+}
+
+.history-head,
+.invite-head {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+ min-height: 58px;
+ padding: 16px 18px 12px;
+ border-bottom: 1px solid #eef0f2;
+}
+
+.history-head h2,
+.invite-head h2 {
+ min-width: 0;
+ margin: 0;
+ overflow: hidden;
+ color: #24282e;
+ font-size: 18px;
+ font-weight: 950;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.history-close,
+.invite-close {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 34px;
+ height: 34px;
+ border-radius: 50%;
+ background: #f4f6f7;
+ color: #50545b;
+ font-size: 25px;
+ font-weight: 500;
+ line-height: 1;
+}
+
+.invite-dialog {
+ max-height: 80vh;
+ max-height: 80dvh;
+}
+
+.invite-body {
+ display: flex;
+ min-height: 0;
+ flex: 1 1 auto;
+ flex-direction: column;
+ gap: 12px;
+ overflow-y: auto;
+ padding: 14px 18px max(18px, env(safe-area-inset-bottom));
+ -webkit-overflow-scrolling: touch;
+}
+
+.invite-search-form {
+ margin: 0;
+}
+
+.invite-field {
+ display: grid;
+ gap: 8px;
+}
+
+.invite-field > span {
+ color: #555b64;
+ font-size: 13px;
+ font-weight: 900;
+ line-height: 1.2;
+}
+
+.invite-search-control {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 8px;
+}
+
+.invite-search-control input {
+ width: 100%;
+ min-width: 0;
+ height: 42px;
+ padding: 0 12px;
+ border: 1px solid #e4e8eb;
+ border-radius: 8px;
+ outline: none;
+ background: #fbfcfc;
+ color: #25282e;
+ font: inherit;
+ font-size: 14px;
+ font-weight: 800;
+}
+
+.invite-search-control input:focus {
+ border-color: rgba(16, 188, 176, 0.72);
+ box-shadow: 0 0 0 3px rgba(67, 231, 216, 0.16);
+}
+
+.invite-search-control button,
+.invite-action,
+.invite-cancel {
+ min-height: 34px;
+ border-radius: 8px;
+ font-size: 12px;
+ font-weight: 950;
+}
+
+.invite-search-control button {
+ min-width: 76px;
+ height: 42px;
+ padding: 0 12px;
+ background: linear-gradient(135deg, #30dfb4, #15bda9);
+ color: #fff;
+}
+
+.invite-search-control button:disabled,
+.invite-action:disabled,
+.invite-cancel:disabled {
+ opacity: 0.55;
+}
+
+.invite-status {
+ padding: 10px 12px;
+ border-radius: 8px;
+ background: #eafbf7;
+ color: #078d80;
+ font-size: 12px;
+ font-weight: 850;
+ line-height: 1.35;
+}
+
+.invite-status.is-error {
+ background: #fff0f0;
+ color: #c43f3f;
+}
+
+.invite-status.is-success {
+ background: #ebfbf2;
+ color: #128946;
+}
+
+.invite-list {
+ display: grid;
+ gap: 10px;
+}
+
+.invite-empty {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ min-height: 86px;
+ border: 1px dashed #e5e8ea;
+ border-radius: 8px;
+ color: #8b8d92;
+ font-size: 14px;
+ font-weight: 850;
+ text-align: center;
+}
+
+.invite-user-card {
+ display: grid;
+ grid-template-columns: 44px minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 10px;
+ padding: 12px;
+ border: 1px solid #eef0f2;
+ border-radius: 8px;
+ background: #fbfcfc;
+}
+
+.invite-user-card.is-selected {
+ border-color: rgba(16, 188, 176, 0.7);
+ box-shadow: 0 0 0 3px rgba(67, 231, 216, 0.14);
+}
+
+.invite-user-avatar {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 44px;
+ height: 44px;
+ overflow: hidden;
+ border-radius: 50%;
+ background: linear-gradient(145deg, #d9fff5, #43e7d8);
+}
+
+.invite-user-avatar img {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.invite-avatar-fallback {
+ color: #0b5f56;
+ font-size: 18px;
+ font-weight: 950;
+}
+
+.invite-user-main {
+ min-width: 0;
+}
+
+.invite-user-name {
+ overflow: hidden;
+ color: #25282e;
+ font-size: 15px;
+ font-weight: 900;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.invite-user-meta {
+ margin-top: 4px;
+ overflow: hidden;
+ color: #8b8d92;
+ font-size: 12px;
+ font-weight: 800;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.invite-user-side {
+ display: grid;
+ justify-items: end;
+ gap: 6px;
+}
+
+.invite-action {
+ min-width: 68px;
+ padding: 0 12px;
+ background: #1fcf7a;
+ color: #fff;
+}
+
+.invite-cancel {
+ min-width: 68px;
+ padding: 0 12px;
+ border: 1px solid #ffd6d6;
+ background: #fff2f2;
+ color: #c74444;
+}
+
+.invite-badge {
+ min-width: 72px;
+ padding: 5px 8px;
+ border-radius: 999px;
+ font-size: 11px;
+ font-weight: 950;
+ line-height: 1;
+ text-align: center;
+}
+
+.invite-badge.pending {
+ background: #fff4dc;
+ color: #bd7a10;
+}
+
+.invite-badge.success {
+ background: #e9fbf1;
+ color: #15914f;
+}
+
+.history-summary {
+ display: grid;
+ gap: 8px;
+ padding: 13px 18px;
+ border-bottom: 1px solid #f0f2f3;
+}
+
+.history-summary.compact {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
+
+.history-summary span {
+ display: block;
+ color: #8b8d92;
+ font-size: 12px;
+ font-weight: 800;
+ line-height: 1.2;
+}
+
+.history-summary strong {
+ display: block;
+ margin-top: 4px;
+ overflow-wrap: anywhere;
+ color: #24282e;
+ font-size: 18px;
+ font-weight: 950;
+ line-height: 1.2;
+}
+
+.history-list {
+ display: grid;
+ flex: 1 1 auto;
+ gap: 10px;
+ min-height: 0;
+ overflow-y: auto;
+ padding: 14px 18px max(18px, env(safe-area-inset-bottom));
+ -webkit-overflow-scrolling: touch;
+}
+
+.history-row {
+ position: relative;
+ padding-top: 35px;
+}
+
+.status-tag {
+ position: absolute;
+ top: 0;
+ left: 0;
+ min-width: 92px;
+ padding: 4px 9px;
+ border-radius: 8px 0 8px 0;
+ background: #3d73ff;
+ color: #fff;
+ font-size: 12px;
+ font-weight: 900;
+ text-align: center;
+}
+
+.history-row.is-completed .status-tag {
+ background: #25c05c;
+}
+
+.history-row.is-pending .status-tag {
+ background: #f0a92e;
+}
+
+.history-row.is-out .status-tag {
+ background: #9ea1a7;
+}
+
+.history-title {
+ color: #24282e;
+ font-size: 15px;
+ font-weight: 950;
+ line-height: 1.2;
+}
+
+.history-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.history-cell span,
+.details-cell span {
+ display: block;
+ color: #96989d;
+ font-size: 11px;
+ font-weight: 800;
+ line-height: 1.2;
+}
+
+.history-cell strong,
+.details-cell strong {
+ display: block;
+ margin-top: 4px;
+ overflow-wrap: anywhere;
+ color: #292d33;
+ font-size: 14px;
+ font-weight: 950;
+ line-height: 1.2;
+}
+
+.more-button {
+ justify-self: end;
+ color: #10bcb0;
+ font-size: 13px;
+ font-weight: 900;
+}
+
+.details-row {
+ grid-template-columns: 44px minmax(0, 1fr);
+ align-items: center;
+}
+
+.loading-mask {
+ position: absolute;
+ z-index: 30;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ flex-direction: column;
+ gap: 18px;
+ min-height: 100vh;
+ background:
+ radial-gradient(circle at 50% 35%, rgba(230, 255, 243, 0.98), rgba(246, 247, 247, 0.98) 44%, rgba(246, 247, 247, 0.96) 100%);
+}
+
+.runner-stick {
+ position: relative;
+ width: 92px;
+ height: 82px;
+ color: #1ecf70;
+ filter: drop-shadow(0 8px 14px rgba(30, 207, 112, 0.22));
+ animation: runner-bob 0.42s ease-in-out infinite;
+}
+
+.runner-head,
+.runner-body,
+.runner-arm,
+.runner-leg,
+.runner-ground {
+ position: absolute;
+ display: block;
+}
+
+.runner-head {
+ top: 5px;
+ left: 39px;
+ width: 18px;
+ height: 18px;
+ border: 4px solid currentColor;
+ border-radius: 50%;
+}
+
+.runner-body {
+ top: 26px;
+ left: 47px;
+ width: 5px;
+ height: 28px;
+ border-radius: 999px;
+ background: currentColor;
+ transform-origin: 50% 3px;
+ animation: runner-body 0.42s ease-in-out infinite;
+}
+
+.runner-arm,
+.runner-leg {
+ left: 47px;
+ width: 5px;
+ border-radius: 999px;
+ background: currentColor;
+ transform-origin: 50% 2px;
+}
+
+.runner-arm {
+ top: 31px;
+ height: 27px;
+}
+
+.runner-arm-front { animation: runner-arm-front 0.42s ease-in-out infinite; }
+.runner-arm-back { animation: runner-arm-back 0.42s ease-in-out infinite; }
+
+.runner-leg {
+ top: 51px;
+ height: 33px;
+}
+
+.runner-leg-front { animation: runner-leg-front 0.42s ease-in-out infinite; }
+.runner-leg-back { animation: runner-leg-back 0.42s ease-in-out infinite; }
+
+.runner-ground {
+ right: 6px;
+ bottom: 2px;
+ width: 70px;
+ height: 4px;
+ overflow: hidden;
+ border-radius: 999px;
+ background: rgba(30, 207, 112, 0.18);
+}
+
+.runner-ground::before,
+.runner-ground::after {
+ content: "";
+ position: absolute;
+ top: 0;
+ width: 24px;
+ height: 4px;
+ border-radius: 999px;
+ background: currentColor;
+ animation: runner-ground 0.6s linear infinite;
+}
+
+.runner-ground::after {
+ animation-delay: 0.3s;
+}
+
+.loading-copy {
+ color: #139a55;
+ font-size: 15px;
+ font-weight: 900;
+ letter-spacing: 0;
+}
+
+.toast {
+ position: fixed;
+ left: 50%;
+ bottom: calc(28px + env(safe-area-inset-bottom));
+ z-index: 50;
+ max-width: 86vw;
+ transform: translateX(-50%);
+ padding: 10px 14px;
+ border-radius: 8px;
+ color: #fff;
+ background: rgba(0, 0, 0, 0.75);
+ font-size: 13px;
+ font-weight: 700;
+ text-align: center;
+}
+
+.home-indicator {
+ position: absolute;
+ left: 50%;
+ bottom: 8px;
+ width: 154px;
+ height: 5px;
+ border-radius: 999px;
+ background: #050506;
+ transform: translateX(-50%);
+}
+
+.modal-open {
+ overflow: hidden;
+}
+
+[dir="rtl"] .content {
+ direction: rtl;
+}
+
+[dir="rtl"] .profile-copy {
+ margin-right: 16px;
+ margin-left: 0;
+ text-align: right;
+}
+
+[dir="rtl"] .member-side {
+ text-align: left;
+}
+
+[dir="rtl"] .status-tag {
+ right: 0;
+ left: auto;
+ border-radius: 0 8px 0 8px;
+}
+
+[dir="rtl"] .language-menu {
+ right: auto;
+ left: 0;
+}
+
+@keyframes runner-bob {
+ 0%,
+ 100% { transform: translateY(0); }
+ 50% { transform: translateY(-5px); }
+}
+
+@keyframes runner-body {
+ 0%,
+ 100% { transform: rotate(-7deg); }
+ 50% { transform: rotate(7deg); }
+}
+
+@keyframes runner-arm-front {
+ 0%,
+ 100% { transform: rotate(62deg); }
+ 50% { transform: rotate(-58deg); }
+}
+
+@keyframes runner-arm-back {
+ 0%,
+ 100% { transform: rotate(-58deg); }
+ 50% { transform: rotate(62deg); }
+}
+
+@keyframes runner-leg-front {
+ 0%,
+ 100% { transform: rotate(-58deg); }
+ 50% { transform: rotate(54deg); }
+}
+
+@keyframes runner-leg-back {
+ 0%,
+ 100% { transform: rotate(54deg); }
+ 50% { transform: rotate(-58deg); }
+}
+
+@keyframes runner-ground {
+ 0% { transform: translateX(78px); }
+ 100% { transform: translateX(-30px); }
+}
+
+@media (max-width: 360px) {
+ .status-bar {
+ padding-right: 22px;
+ padding-left: 30px;
+ }
+
+ .content {
+ padding-right: 12px;
+ padding-left: 12px;
+ }
+
+ .member-row {
+ grid-template-columns: 40px minmax(0, 1fr);
+ }
+
+ .member-side {
+ grid-column: 2;
+ text-align: left;
+ }
+}
diff --git a/h5/hyapp/bd-leader-center/index.html b/h5/hyapp/bd-leader-center/index.html
index 8052167..1f674dc 100644
--- a/h5/hyapp/bd-leader-center/index.html
+++ b/h5/hyapp/bd-leader-center/index.html
@@ -4,7 +4,7 @@
BD Leader Center
-
+
@@ -54,7 +54,6 @@
@@ -97,7 +96,7 @@
-
+
-
+