export const API_BASE_URL = "https://jvapi.haiyihy.com"; const text = { en: { bd_leader_center: "BD Leader Center", available_salaries: "Available salaries", bd: "BD", bd_leader: "BD Leader", my_bd_salary: "My BD salary", my_bd_leader_salary: "My BD leader salary", exchange: "Exchange", transfer: "Transfer", cash_out: "Cash out", details: "Details", please_select_amount: "Please select amount", please_select_coin_amount: "Please select coin amount", please_select_payee: "Please select payee", transfer_dollar_success: "Transfer succeeded", transfer_dollar_failed: "Transfer failed", transfer_error: "Transfer error", exchange_success: "Exchange succeeded", exchange_error_occurred: "Exchange failed", amount: "Amount", coins: "Coins", dollar: "Dollar", minimum_cash_out: "Minimum cash out is $50", bd_policy_link: "BD policy", bd_leader_policy: "BD leader policy", bd_policy: "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", bank_card: "Bank Card", kyc: "KYC", category: "Category", card_number_label: "Card number", card_number: "Card number", payee: "Payee", more: "More", add_payment_method: "Add payment method", complete_information: "Complete information", passport_id_card_title: "Passport/ID card", contact_number: "Contact number", other_description: "Other description", review_passed: "Review passed", review_failed: "Review failed", awaiting_review: "Awaiting review", currently_in_use: "Currently in use", all_bank_cards: "All bank cards", no_bank_cards_in_use: "No bank cards in use", no_bank_cards_added: "No bank cards added", add_new_payment_method: "Add new payment method", withdrawal_method: "Withdrawal method", choose_withdrawal_method: "Choose withdrawal method", card_name: "Card name", enter_card_name: "Enter card name", enter_card_number: "Enter card number", enter_payee: "Enter payee", delete: "Delete", use: "Use", using: "Using", confirm: "Confirm", submit: "Submit", change_kyc: "Change KYC", enter_contact_number: "Enter contact number", enter_other_description: "Enter other description", current_status: "Current status", tips: "Tips", confirm_modify_kyc: "Confirm modify KYC?", cancel: "Cancel", application_submitted: "Application submitted", agency_number: "Agency number", number_of_bd: "BD number", bd_number: "BD number", team_salary: "Team salary", team_recharge: "Team recharge", salary: "Salary", recharge: "Recharge", host: "Host", agencies: "Agencies", user_id_prefix: "ID: ", revenue_details: "Revenue details", bd_leader_history: "BD Leader history", total_income: "Total income", previous_period_income: "Previous period income", income_details_label: "Income details", team_top_up: "Team top up", bd_income_salary: "BD income salary", bd_income_top_up: "BD income top up", total_team_salary: "Total team salary", num_teams: "Num teams", in_progress: "In Progress", pending: "Pending", completed: "Completed", out_of_account: "Out of account", information: "Information", enter_user_id: "Enter User ID", invite: "Invite", no_users_found: "No users found", success: "Success", invitation_submitted: "Invitation submitted", application_cancelled: "Application cancelled", something_went_wrong: "Something went wrong", cancellation_failed_try_again: "Cancellation failed, try again" , item_distribution: "Item distribution", frames: "Frames", vehicles: "Vehicles", vip: "VIP", send: "Send", validity: "Validity", day: " day", enter_user_id_placeholder: "Enter user ID", gift_delivery_successful: "Gift delivery successful", available_salaries: "Available salaries", details: "Details", bd: "BD", bd_leader: "BD Leader", my_bd_salary: "My BD salary", my_bd_leader_salary: "My BD leader salary", exchange: "Exchange", transfer: "Transfer", cash_out: "Cash out", please_select_amount: "Please select amount", please_select_payee: "Please select payee", transfer_dollar_success: "Transfer successful", transfer_dollar_failed: "Transfer failed", transfer_error: "Transfer error", please_select_coin_amount: "Please select coin amount", exchange_success: "Exchange successful", exchange_error_occurred: "Exchange failed", confirm_exchange: "Confirm exchange?", fill_info_tips: "Please complete bank card and KYC information", card_issue_tips: "Bank card information is required before cash out", to_improve: "To improve", cash_withdrawal_tips: "Cash withdrawal submitted", enter_cash_out_amount: "Enter cash out amount", cash_out_minimum: "Cash out minimum: $50", change_withdrawal_method: "Change withdrawal method", please_select_bank_card: "Please select bank card", prompt: "Prompt", failed_to_load_balance: "Failed to load balance" }, ar: {}, tr: {}, bn: {} }; ["ar", "tr", "bn"].forEach((lang) => { text[lang] = { ...text.en }; }); let headers = { authorization: "", "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", "content-type": "application/json" }; export const lang = resolveLanguage(); let appConnectionPromise = null; export function t(key) { return text[lang]?.[key] || text.en[key] || key; } export function applyLang() { document.documentElement.lang = lang; document.documentElement.dir = lang === "ar" ? "rtl" : "ltr"; document.querySelectorAll("[data-i18n]").forEach((node) => { node.textContent = t(node.dataset.i18n); }); const language = document.querySelector("[data-action='language']"); if (language) language.textContent = lang.toUpperCase(); } function resolveLanguage() { const params = new URLSearchParams(window.location.search); const hash = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : ""; const hashParams = new URLSearchParams(hash); const value = params.get("lang") || hashParams.get("lang") || navigator.language || "en"; if (value.startsWith("ar")) return "ar"; if (value.startsWith("tr")) return "tr"; if (value.startsWith("bn")) return "bn"; return "en"; } 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 parseAppHeaders(raw) { const data = JSON.parse(raw); const appIntel = splitHeaderPairs(data["Req-App-Intel"]); const sysOrigin = splitHeaderPairs(data["Req-Sys-Origin"]); return { authorization: data.Authorization || data.authorization, reqLang: data["Req-Lang"] || data.reqLang || data.lang, appVersion: appIntel.version || data["App-Version"] || data.appVersion, buildVersion: appIntel.build || data["Build-Version"] || data.buildVersion, channel: appIntel.channel || data["App-Channel"] || data.appChannel || data.channel, reqImei: data["Req-Imei"] || data.reqImei || appIntel["Req-Imei"], origin: sysOrigin.origin || data["Sys-Origin"] || data.sysOrigin, child: sysOrigin.originChild || sysOrigin.child || data["Sys-Origin-Child"] || data.sysOriginChild }; } export function setHeadersFromApp(rawOrObject) { const data = typeof rawOrObject === "string" ? parseAppHeaders(rawOrObject) : rawOrObject || {}; const next = {}; if (Object.prototype.hasOwnProperty.call(data, "authorization")) next.authorization = data.authorization || ""; if (data.reqLang) next["req-lang"] = data.reqLang; if (data.appVersion || data.buildVersion || data.channel) { next["req-app-intel"] = [ data.appVersion ? `version=${data.appVersion}` : "", data.buildVersion ? `build=${data.buildVersion}` : "", data.channel ? `channel=${data.channel}` : "" ].filter(Boolean).join(";"); } if (data.reqImei) next["req-imei"] = data.reqImei; if (data.origin) next["req-sys-origin"] = [`origin=${data.origin}`, data.child ? `child=${data.child}` : ""].filter(Boolean).join(";"); headers = { ...headers, ...next }; } function isAppEnvironment() { return Boolean(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp); } function requestAccessOrigin() { return new Promise((resolve) => { let settled = false; let timeoutId = null; const finish = (raw) => { if (settled) { if (raw) setHeadersFromApp(raw); return; } settled = true; if (timeoutId) window.clearTimeout(timeoutId); resolve(raw || null); }; window.renderData = finish; window.getIosAccessOriginParam = finish; if (!isAppEnvironment()) { setHeadersFromApp({ authorization: "", reqLang: lang, appVersion: "1.0.0", buildVersion: "1", channel: "Web", reqImei: "H5-STATIC", origin: "LIKEI", child: "LIKEI" }); finish(null); return; } const requestFromFlutter = () => { if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") { window.FlutterApp.postMessage("requestAccessOrigin"); } }; if (window.app && typeof window.app.getAccessOrigin === "function") { finish(window.app.getAccessOrigin()); return; } requestFromFlutter(); timeoutId = window.setTimeout(() => finish(null), 12000); let attempt = 0; const poll = () => { attempt += 1; if (window.app && typeof window.app.getAccessOrigin === "function") { finish(window.app.getAccessOrigin()); return; } if (attempt < 50) { window.setTimeout(poll, 100); return; } requestFromFlutter(); }; poll(); }); } export async function connectToApp() { if (!appConnectionPromise) { appConnectionPromise = requestAccessOrigin() .then((raw) => { if (raw) setHeadersFromApp(raw); return { success: true, environment: isAppEnvironment() ? "app" : "browser" }; }) .finally(() => { appConnectionPromise = null; }); } return appConnectionPromise; } export async function request(method, path, body) { const options = { method, headers }; if (body !== undefined) options.body = JSON.stringify(body); const response = await fetch(`${API_BASE_URL}${path}`, options); 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; } export const get = (path) => request("GET", path); export const post = (path, body) => request("POST", path, body); export 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}`; } export function closePage() { const params = new URLSearchParams(window.location.search); const from = params.get("from"); if (from) { const target = new URL(from, window.location.origin); const activeAction = params.get("activeAction"); if (activeAction) target.searchParams.set("activeAction", activeAction); const currentLang = params.get("lang"); if (currentLang && !target.searchParams.has("lang")) target.searchParams.set("lang", currentLang); window.location.href = `${target.pathname}${target.search}${target.hash}`; return; } 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"); } } export function bindHeader(titleKey) { document.body.insertAdjacentHTML("afterbegin", `

${t(titleKey)}

`); document.querySelector("[data-action='back']").addEventListener("click", closePage); document.querySelector("[data-action='language']").addEventListener("click", () => navigateTo("/language")); applyLang(); } export function money(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); } export function safeJsonArray(value) { try { const result = JSON.parse(value || "[]"); return Array.isArray(result) ? result : []; } catch { return []; } } export function getUserId() { const keys = ["userInfo", "user", "USER_INFO"]; for (const key of keys) { try { const raw = localStorage.getItem(key) || sessionStorage.getItem(key); if (!raw) continue; const data = JSON.parse(raw); if (data.id || data.userId) return data.id || data.userId; } catch {} } return ""; } export function toast(message) { let node = document.getElementById("toast"); if (!node) { node = document.createElement("div"); node.id = "toast"; node.className = "toast"; document.body.appendChild(node); } node.textContent = message; node.hidden = false; window.clearTimeout(toast.timer); toast.timer = window.setTimeout(() => { node.hidden = true; }, 2500); } export function createMask(content) { const old = document.getElementById("modalMask"); if (old) old.remove(); const mask = document.createElement("div"); mask.id = "modalMask"; mask.className = "mask"; mask.addEventListener("click", () => mask.remove()); content.addEventListener("click", (event) => event.stopPropagation()); mask.appendChild(content); document.body.appendChild(mask); return mask; } export function statusClass(status) { if (status === "Completed") return "completed"; if (status === "Pending") return "pending"; if (status === "Out of account" || status === "HANG_UP") return "out"; return ""; } export function statusText(status) { const map = { SETTLED: t("completed"), PAY_OUT: t("completed"), UNPAID: t("in_progress"), HANG_UP: t("out_of_account"), "In Progress": t("in_progress"), Pending: t("pending"), Completed: t("completed"), "Out of account": t("out_of_account") }; return map[status] || status || ""; } export function avatarError(img) { img.addEventListener("error", () => { img.removeAttribute("src"); }); }