(function () { const API_BASE_URL = "https://jvapi.haiyihy.com"; const supportedLanguages = ["en", "ar", "tr", "id"]; const languageLabels = { en: "EN", ar: "AR", tr: "TR", id: "ID" }; const translations = { en: { document_title: "Salary Bills", page_label: "Salary Bills", back: "Back", title: "Salary Bills", language_button_aria: "Change language", history_team_income: "History team income", bill_summary: "Bill summary", total_bills: "Total bills", completed_bills: "Completed", work_report: "Work report", host_salary: "Host salary", agent_salary: "Agent salary", total: "Total", more: "More", team_member: "Team member", enter_host_id: "Enter host ID", search: "Search", cancel: "Cancel", close: "Close", help: "Help", open_help: "Open help", work_report_description: "Work report shows each team bill cycle and salary summary.", status_description: "Status describes the current settlement state.", status_completed: "Completed: salary has been settled.", status_in_progress: "In progress: the bill is still running.", status_out_of_account: "Out of account: the bill is suspended or unavailable.", completed: "Completed", in_progress: "In progress", out_of_account: "Out of account", pending: "Pending", target: "Target", days: "Days", uid_prefix: "UID:", gift_points: "Gift points", loading: "Loading", no_bills: "No bills", no_members: "No hosts", no_search_result: "No matching hosts", missing_token: "Missing token", missing_token_text: "Open this page from the app or add a token in the URL.", missing_team: "Missing team information", failed_to_load_data: "Failed to load data", unknown_user: "Unknown user" }, ar: { document_title: "Salary Bills", page_label: "Salary Bills", back: "رجوع", title: "Salary Bills", language_button_aria: "تغيير اللغة", history_team_income: "دخل الفريق السابق", bill_summary: "ملخص الفواتير", total_bills: "إجمالي الفواتير", completed_bills: "مكتمل", work_report: "تقرير العمل", host_salary: "راتب المضيف", agent_salary: "راتب الوكالة", total: "الإجمالي", more: "المزيد", team_member: "عضو الفريق", enter_host_id: "أدخل معرف المضيف", search: "بحث", cancel: "إلغاء", close: "إغلاق", help: "مساعدة", open_help: "فتح المساعدة", work_report_description: "يعرض تقرير العمل كل دورة فاتورة للفريق وملخص الراتب.", status_description: "توضح الحالة وضع التسوية الحالي.", status_completed: "مكتمل: تم تسوية الراتب.", status_in_progress: "قيد التنفيذ: لا تزال الفاتورة قيد التشغيل.", status_out_of_account: "خارج الحساب: الفاتورة موقوفة أو غير متاحة.", completed: "مكتمل", in_progress: "قيد التنفيذ", out_of_account: "خارج الحساب", pending: "قيد الانتظار", target: "الهدف", days: "الأيام", uid_prefix: "UID:", gift_points: "نقاط الهدايا", loading: "جار التحميل", no_bills: "لا توجد فواتير", no_members: "لا يوجد مضيفون", no_search_result: "لا توجد نتائج مطابقة", missing_token: "رمز الدخول مفقود", missing_token_text: "افتح هذه الصفحة من التطبيق أو أضف token في الرابط.", missing_team: "معلومات الفريق مفقودة", failed_to_load_data: "فشل تحميل البيانات", unknown_user: "مستخدم غير معروف" }, tr: { document_title: "Salary Bills", page_label: "Salary Bills", back: "Geri", title: "Salary Bills", language_button_aria: "Dili degistir", history_team_income: "Gecmis takim geliri", bill_summary: "Fatura ozeti", total_bills: "Toplam fatura", completed_bills: "Tamamlandi", work_report: "Calisma raporu", host_salary: "Host maasi", agent_salary: "Ajans maasi", total: "Toplam", more: "Daha fazla", team_member: "Takim uyesi", enter_host_id: "Host ID girin", search: "Ara", cancel: "Iptal", close: "Kapat", help: "Yardim", open_help: "Yardimi ac", work_report_description: "Calisma raporu her takim fatura dongusunu ve maas ozetini gosterir.", status_description: "Durum mevcut odeme surecini aciklar.", status_completed: "Tamamlandi: maas odendi.", status_in_progress: "Devam ediyor: fatura hala calisiyor.", status_out_of_account: "Hesap disi: fatura askida veya kullanilamiyor.", completed: "Tamamlandi", in_progress: "Devam ediyor", out_of_account: "Hesap disi", pending: "Beklemede", target: "Hedef", days: "Gun", uid_prefix: "UID:", gift_points: "Hediye puani", loading: "Yukleniyor", no_bills: "Fatura yok", no_members: "Host yok", no_search_result: "Eslesen host yok", missing_token: "Token eksik", missing_token_text: "Bu sayfayi uygulamadan acin veya URL'ye token ekleyin.", missing_team: "Takim bilgisi eksik", failed_to_load_data: "Veri yuklenemedi", unknown_user: "Bilinmeyen kullanici" }, id: { document_title: "Salary Bills", page_label: "Salary Bills", back: "Kembali", title: "Salary Bills", language_button_aria: "Ubah bahasa", history_team_income: "Riwayat pendapatan tim", bill_summary: "Ringkasan tagihan", total_bills: "Total tagihan", completed_bills: "Selesai", work_report: "Laporan kerja", host_salary: "Gaji host", agent_salary: "Gaji agensi", total: "Total", more: "Lainnya", team_member: "Anggota tim", enter_host_id: "Masukkan ID host", search: "Cari", cancel: "Batal", close: "Tutup", help: "Bantuan", open_help: "Buka bantuan", work_report_description: "Laporan kerja menampilkan setiap siklus tagihan tim dan ringkasan gaji.", status_description: "Status menjelaskan kondisi penyelesaian saat ini.", status_completed: "Selesai: gaji telah diselesaikan.", status_in_progress: "Berjalan: tagihan masih berjalan.", status_out_of_account: "Di luar akun: tagihan ditangguhkan atau tidak tersedia.", completed: "Selesai", in_progress: "Berjalan", out_of_account: "Di luar akun", pending: "Tertunda", target: "Target", days: "Hari", uid_prefix: "UID:", gift_points: "Poin hadiah", loading: "Memuat", no_bills: "Tidak ada tagihan", no_members: "Tidak ada host", no_search_result: "Tidak ada host yang cocok", missing_token: "Token tidak ditemukan", missing_token_text: "Buka halaman ini dari aplikasi atau tambahkan token di URL.", missing_team: "Informasi tim tidak ditemukan", failed_to_load_data: "Gagal memuat data", unknown_user: "Pengguna tidak dikenal" } }; const params = readURLParams(); let currentLang = normalizeLanguage(params.get("lang")); let currentMessages = translations[currentLang] || translations.en; let currentTeamId = params.get("teamId") || params.get("id") || ""; let bills = []; let currentPolicy = []; let selectedBill = null; let selectedMembers = []; let displayMembers = []; let memberSearchActive = false; let detailLoading = false; function readURLParams() { const result = new URLSearchParams(window.location.search); const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : ""; const hashParams = new URLSearchParams(hashQuery); hashParams.forEach((value, key) => { if (!result.has(key)) result.set(key, value); }); return result; } function readRawParam(name) { const queries = [window.location.search.replace(/^\?/, "")]; if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]); for (const query of queries) { for (const part of query.split("&")) { if (!part) continue; const equalIndex = part.indexOf("="); const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part; const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : ""; let decodedKey = ""; try { decodedKey = decodeURIComponent(rawKey); } catch (error) { continue; } if (decodedKey !== name) continue; try { return decodeURIComponent(rawValue.replace(/\+/g, "%2B")); } catch (error) { return rawValue; } } } return ""; } function normalizeLanguage(lang) { const value = String(lang || "en").toLowerCase(); return supportedLanguages.includes(value) ? value : "en"; } function t(key, fallback) { return currentMessages[key] || translations.en[key] || fallback || key; } 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(t("missing_token", "Missing token")); const headers = authHeadersFromToken(token); const method = options.method || "GET"; const hasBody = options.body !== undefined; if (hasBody) headers["Content-Type"] = "application/json"; const response = await fetch(new URL(path, `${apiBaseURL()}/`).toString(), { method, cache: "no-store", headers, body: hasBody ? JSON.stringify(options.body) : undefined }); 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 buildPageURL(target) { const url = new URL(target, window.location.href); const nextParams = new URLSearchParams(url.search); params.forEach((value, key) => { if (!nextParams.has(key)) nextParams.set(key, value); }); const token = readRawParam("token"); if (token) nextParams.set("token", token); if (currentLang) nextParams.set("lang", currentLang); if (currentTeamId) nextParams.set("teamId", currentTeamId); url.search = nextParams.toString(); return url.toString(); } function setLoading(isLoading) { const root = document.querySelector(".hosts-bill-page"); const loadingMask = document.querySelector("#loadingMask"); if (root) root.dataset.loading = isLoading ? "true" : "false"; if (loadingMask) { loadingMask.hidden = !isLoading; loadingMask.setAttribute("aria-busy", String(isLoading)); } } function renderLanguageControls() { const languageButton = document.querySelector(".language-button"); if (languageButton) { languageButton.textContent = languageLabels[currentLang]; languageButton.setAttribute("aria-label", t("language_button_aria")); } document.querySelectorAll(".language-menu [data-lang]").forEach((button) => { const isActive = button.dataset.lang === currentLang; button.classList.toggle("is-active", isActive); button.setAttribute("aria-pressed", String(isActive)); }); } function renderMessages() { document.querySelectorAll("[data-i18n]").forEach((node) => { const value = currentMessages[node.dataset.i18n]; if (value) node.textContent = value; }); document.querySelectorAll("[data-i18n-aria]").forEach((node) => { const value = currentMessages[node.dataset.i18nAria]; if (value) node.setAttribute("aria-label", value); }); document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => { const value = currentMessages[node.dataset.i18nPlaceholder]; if (value) node.setAttribute("placeholder", value); }); } function applyLanguage(lang) { currentLang = normalizeLanguage(lang); currentMessages = translations[currentLang] || translations.en; document.documentElement.lang = currentLang; document.documentElement.dir = currentLang === "ar" ? "rtl" : "ltr"; document.title = t("document_title"); renderMessages(); renderLanguageControls(); renderSummary(); renderBills(); renderBillDetail(); } function setLanguageMenuOpen(open) { const languageButton = document.querySelector(".language-button"); const languageMenu = document.querySelector(".language-menu"); if (!languageButton || !languageMenu) return; languageMenu.hidden = !open; languageButton.setAttribute("aria-expanded", String(open)); } function pickTeamInfo(body) { return body?.teamProfile || body || null; } function pickTeamId(teamInfo) { return teamInfo?.id || teamInfo?.teamId || teamInfo?.teamProfile?.id || ""; } function pickRecords(body) { if (Array.isArray(body)) return body; if (Array.isArray(body?.records)) return body.records; if (Array.isArray(body?.list)) return body.list; if (Array.isArray(body?.items)) return body.items; return []; } function pickPolicyRecords(body) { if (Array.isArray(body)) return body; if (Array.isArray(body?.policy)) return body.policy; if (Array.isArray(body?.policies)) return body.policies; return pickRecords(body); } function formatMoney(value) { const number = Number(value); return Number.isFinite(number) ? `$${number.toFixed(2)}` : "$0.00"; } function formatPlainMoney(value) { const number = Number(value); return Number.isFinite(number) ? number.toFixed(2) : "0.00"; } function formatCount(value) { if (value === null || value === undefined || value === "") return "0"; const number = Number(value); if (!Number.isFinite(number)) return String(value); return Math.trunc(number).toLocaleString("en-US"); } function formatGift(value) { if (value === null || value === undefined || value === "") return "0"; const number = Number(value); if (!Number.isFinite(number)) return String(value); return Math.trunc(number).toLocaleString("en-US"); } function normalizeStatus(status) { switch (String(status || "").toUpperCase()) { case "SETTLED": case "PAY_OUT": case "COMPLETED": return { key: "completed", className: "status-completed" }; case "HANG_UP": case "OUT_OF_ACCOUNT": case "OUT OF ACCOUNT": return { key: "out_of_account", className: "status-out-of-account" }; case "PENDING": return { key: "pending", className: "status-pending" }; case "UNPAID": case "IN_PROGRESS": case "IN PROGRESS": default: return { key: "in_progress", className: "status-in-progress" }; } } function statusText(status) { const info = normalizeStatus(status); return t(info.key, status || "-"); } function setStatusBadge(node, status) { if (!node) return; const info = normalizeStatus(status); node.className = `bill-status-badge ${info.className}`; node.textContent = t(info.key); } function billTitle(bill) { return bill?.billTitle || bill?.billDayTitle || String(bill?.billBelong || "-"); } function billTotal(bill) { return Number(bill?.settleResult?.totalSalary || 0); } function billMemberSalary(bill) { return Number(bill?.settleResult?.memberSalary || 0); } function billAgentSalary(bill) { return Number(bill?.settleResult?.ownSalary || 0); } function isCompletedBill(bill) { const status = String(bill?.status || "").toUpperCase(); return status === "SETTLED" || status === "PAY_OUT" || status === "COMPLETED"; } function calculateHistoryIncome() { return bills.reduce((sum, bill) => { if (!isCompletedBill(bill)) return sum; return sum + billTotal(bill); }, 0); } function replaceNodeChildren(node, children) { while (node.firstChild) node.removeChild(node.firstChild); children.forEach((child) => node.appendChild(child)); } function createSvg(paths) { const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("viewBox", "0 0 24 24"); svg.setAttribute("aria-hidden", "true"); paths.forEach((pathValue) => { const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", pathValue); svg.appendChild(path); }); return svg; } function renderSummary() { const historyIncome = document.querySelector("#historyIncome"); const totalBills = document.querySelector("#totalBills"); const completedBills = document.querySelector("#completedBills"); if (historyIncome) historyIncome.textContent = formatMoney(calculateHistoryIncome()); if (totalBills) totalBills.textContent = formatCount(bills.length); if (completedBills) completedBills.textContent = formatCount(bills.filter(isCompletedBill).length); } function createMoneyItem(label, value) { const item = document.createElement("div"); item.className = "bill-money-item"; const labelNode = document.createElement("span"); labelNode.textContent = label; const valueNode = document.createElement("strong"); valueNode.textContent = formatMoney(value); item.appendChild(labelNode); item.appendChild(valueNode); return item; } function createBillCard(bill) { const card = document.createElement("article"); card.className = "bill-card"; const top = document.createElement("div"); top.className = "bill-card-top"; const date = document.createElement("div"); date.className = "bill-date"; date.textContent = billTitle(bill); const badge = document.createElement("span"); setStatusBadge(badge, bill?.status); top.appendChild(date); top.appendChild(badge); const moneyGrid = document.createElement("div"); moneyGrid.className = "bill-money-grid"; moneyGrid.appendChild(createMoneyItem(t("host_salary"), billMemberSalary(bill))); moneyGrid.appendChild(createMoneyItem(t("agent_salary"), billAgentSalary(bill))); moneyGrid.appendChild(createMoneyItem(t("total"), billTotal(bill))); const more = document.createElement("button"); more.type = "button"; more.className = "bill-more-button"; more.textContent = t("more"); more.addEventListener("click", () => { openBillDetail(bill); }); card.appendChild(top); card.appendChild(moneyGrid); card.appendChild(more); return card; } function setListStatus(message) { const status = document.querySelector("#listStatus"); if (status) status.textContent = message || ""; } function renderBills() { renderSummary(); const list = document.querySelector("#billList"); if (!list) return; replaceNodeChildren(list, bills.map(createBillCard)); setListStatus(bills.length ? "" : t("no_bills")); } function pickMemberProfile(member) { return member?.memberProfile || member?.userProfile || member?.profile || member?.user || {}; } function pickMemberTarget(member) { return member?.target || member?.memberTarget || {}; } function pickMemberName(profile, member) { return profile?.userNickname || profile?.name || member?.userNickname || member?.name || t("unknown_user"); } function pickMemberAccount(profile, member) { return profile?.ownSpecialId?.account || profile?.account || member?.account || member?.userAccount || profile?.id || member?.userId || "-"; } function memberIdentifiers(member) { const profile = pickMemberProfile(member); return [ profile?.id, profile?.account, profile?.ownSpecialId?.account, member?.userId, member?.account, member?.userAccount ] .filter((value) => value !== null && value !== undefined && value !== "") .map((value) => String(value)); } function calculateTargetLevel(acceptGiftValue) { if (!Array.isArray(currentPolicy) || !currentPolicy.length) return "N/A"; const giftValue = Number(acceptGiftValue); if (!Number.isFinite(giftValue)) return "N/A"; const sortedPolicy = currentPolicy .slice() .filter((item) => item && item.target !== null && item.target !== undefined) .sort((left, right) => Number(right.target) - Number(left.target)); for (const policy of sortedPolicy) { if (giftValue >= Number(policy.target)) return policy.level ?? "N/A"; } const levels = sortedPolicy .map((policy) => Number(policy.level)) .filter((level) => Number.isFinite(level)); return levels.length ? Math.min(...levels) : "N/A"; } function createMemberAvatar(name, avatarURL) { const avatar = document.createElement("div"); avatar.className = "bill-member-avatar"; const fallback = document.createElement("span"); fallback.textContent = (name || t("unknown_user")).slice(0, 1).toUpperCase(); avatar.appendChild(fallback); if (avatarURL) { const image = document.createElement("img"); image.src = avatarURL; image.alt = name; image.addEventListener("error", () => { image.remove(); }); avatar.appendChild(image); } return avatar; } function createMemberCard(member) { const profile = pickMemberProfile(member); const target = pickMemberTarget(member); const name = pickMemberName(profile, member); const account = pickMemberAccount(profile, member); const giftValue = target?.acceptGiftValue || 0; const effectiveDay = target?.effectiveDay || 0; const card = document.createElement("article"); card.className = "bill-member-card"; const user = document.createElement("div"); user.className = "bill-member-user"; user.appendChild(createMemberAvatar(name, profile?.userAvatar || member?.userAvatar || member?.avatar || "")); const copy = document.createElement("div"); copy.className = "bill-member-copy"; const nameNode = document.createElement("div"); nameNode.className = "bill-member-name"; nameNode.textContent = name; const accountNode = document.createElement("div"); accountNode.className = "bill-member-account"; accountNode.textContent = `${t("uid_prefix")} ${account}`.trim(); copy.appendChild(nameNode); copy.appendChild(accountNode); user.appendChild(copy); const side = document.createElement("div"); side.className = "bill-member-side"; const level = document.createElement("span"); level.className = "bill-member-target"; level.textContent = `${t("target")}: ${calculateTargetLevel(giftValue)}`; const gift = document.createElement("div"); gift.className = "bill-member-gift"; gift.textContent = formatGift(giftValue); gift.title = `${t("gift_points")}: ${formatGift(giftValue)}`; const days = document.createElement("div"); days.className = "bill-member-days"; days.textContent = `${t("days")}: ${effectiveDay}`; side.appendChild(level); side.appendChild(gift); side.appendChild(days); card.appendChild(user); card.appendChild(side); return card; } function setMemberStatus(message) { const status = document.querySelector("#memberStatus"); if (status) status.textContent = message || ""; } function renderSearchState() { const cancelButton = document.querySelector("#memberCancelButton"); if (cancelButton) cancelButton.hidden = !memberSearchActive; } function renderBillDetail() { const date = document.querySelector("#detailBillDate"); const total = document.querySelector("#detailBillTotal"); const status = document.querySelector("#detailBillStatus"); const list = document.querySelector("#billMemberList"); if (date) date.textContent = selectedBill ? billTitle(selectedBill) : "-"; if (total) total.textContent = selectedBill ? formatMoney(billTotal(selectedBill)) : "$0.00"; if (status && selectedBill) setStatusBadge(status, selectedBill.status); if (list) replaceNodeChildren(list, displayMembers.map(createMemberCard)); renderSearchState(); if (detailLoading) { setMemberStatus(`${t("loading")}...`); return; } if (!displayMembers.length) { setMemberStatus(memberSearchActive ? t("no_search_result") : t("no_members")); return; } setMemberStatus(""); } async function ensureTeamId() { if (currentTeamId) return currentTeamId; const body = await requestJSON("/team/entry"); currentTeamId = pickTeamId(pickTeamInfo(body)); return currentTeamId; } async function fetchBills() { const query = new URLSearchParams({ id: String(currentTeamId) }); const body = await requestJSON(`/team/bill?${query.toString()}`); bills = pickRecords(body); renderBills(); } async function fetchPolicy() { const query = new URLSearchParams({ id: String(currentTeamId) }); const body = await requestJSON(`/team/release/policy?${query.toString()}`); currentPolicy = pickPolicyRecords(body); } async function fetchBillMembers(billId) { const query = new URLSearchParams({ id: String(billId) }); const body = await requestJSON(`/team/bill/work-members?${query.toString()}`); return pickRecords(body); } async function openBillDetail(bill) { selectedBill = bill; selectedMembers = []; displayMembers = []; memberSearchActive = false; detailLoading = true; const input = document.querySelector("#memberSearchInput"); if (input) input.value = ""; const modal = document.querySelector("#billDetailModal"); if (modal) modal.hidden = false; document.body.classList.add("daily-modal-open"); renderBillDetail(); try { selectedMembers = await fetchBillMembers(bill.id); displayMembers = selectedMembers.slice(); } catch (error) { console.error("Failed to load bill members:", error); showToast(error.message || t("failed_to_load_data")); selectedMembers = []; displayMembers = []; } finally { detailLoading = false; renderBillDetail(); } } function setBillDetailOpen(open) { const modal = document.querySelector("#billDetailModal"); if (!modal) return; modal.hidden = !open; document.body.classList.toggle("daily-modal-open", open || isHelpOpen()); if (!open) { selectedBill = null; selectedMembers = []; displayMembers = []; memberSearchActive = false; detailLoading = false; } } function isHelpOpen() { return !document.querySelector("#helpModal")?.hidden; } function setHelpOpen(open) { const modal = document.querySelector("#helpModal"); if (!modal) return; modal.hidden = !open; document.body.classList.toggle("daily-modal-open", open || !document.querySelector("#billDetailModal")?.hidden); } function searchMembers() { const input = document.querySelector("#memberSearchInput"); const query = String(input?.value || "").trim(); if (!query) { cancelMemberSearch(); return; } memberSearchActive = true; displayMembers = selectedMembers.filter((member) => memberIdentifiers(member).some((id) => id === query)); renderBillDetail(); } function cancelMemberSearch() { const input = document.querySelector("#memberSearchInput"); if (input) input.value = ""; memberSearchActive = false; displayMembers = selectedMembers.slice(); renderBillDetail(); } function showToast(message) { const toast = document.querySelector("#toast"); if (!toast) return; toast.textContent = message; toast.hidden = false; window.clearTimeout(showToast.timer); showToast.timer = window.setTimeout(() => { toast.hidden = true; }, 2200); } function goBack() { if (window.history.length > 1) { window.history.back(); return; } window.location.href = buildPageURL("../agency-center/index.html"); } function bindEvents() { document.querySelector(".back-button")?.addEventListener("click", goBack); document.querySelector(".language-button")?.addEventListener("click", (event) => { event.stopPropagation(); const languageMenu = document.querySelector(".language-menu"); setLanguageMenuOpen(Boolean(languageMenu?.hidden)); }); document.querySelectorAll(".language-menu [data-lang]").forEach((button) => { button.addEventListener("click", (event) => { event.stopPropagation(); setLanguageMenuOpen(false); applyLanguage(button.dataset.lang); }); }); document.querySelector(".bill-help-button")?.addEventListener("click", () => { setHelpOpen(true); }); document.querySelector("#memberSearchForm")?.addEventListener("submit", (event) => { event.preventDefault(); searchMembers(); }); document.querySelector("#memberCancelButton")?.addEventListener("click", cancelMemberSearch); document.querySelectorAll("[data-close-detail]").forEach((node) => { node.addEventListener("click", () => setBillDetailOpen(false)); }); document.querySelectorAll("[data-close-help]").forEach((node) => { node.addEventListener("click", () => setHelpOpen(false)); }); document.addEventListener("click", () => { setLanguageMenuOpen(false); }); document.addEventListener("keydown", (event) => { if (event.key === "Escape") { setLanguageMenuOpen(false); setBillDetailOpen(false); setHelpOpen(false); } }); } async function initializePage() { setLoading(true); applyLanguage(currentLang); bindEvents(); if (!readRawParam("token")) { setLoading(false); setListStatus(t("missing_token_text")); return; } try { await ensureTeamId(); if (!currentTeamId) throw new Error(t("missing_team")); const results = await Promise.allSettled([ fetchBills(), fetchPolicy() ]); results.forEach((result) => { if (result.status === "rejected") console.warn("Hosts bill request failed:", result.reason); }); } catch (error) { console.error("Failed to initialize hosts bill:", error); showToast(error.message || t("failed_to_load_data")); setListStatus(error.message || t("failed_to_load_data")); } finally { setLoading(false); } } initializePage(); })();