385 lines
13 KiB
JavaScript
385 lines
13 KiB
JavaScript
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: "Bearer undefined",
|
|
"req-app-intel": "version=1.0.0;build=1;model=SM-G9550;sysVersion=9;channel=Google",
|
|
"req-client": "Android",
|
|
"req-imei": "8fa54d728ab449e04f9292329ed44bda",
|
|
"req-lang": "id-ID",
|
|
"req-sys-origin": "origin=ATYOU;originChild=ATYOU",
|
|
"req-version": "V2",
|
|
"req-zone": "Asia/Shanghai",
|
|
"content-type": "application/json"
|
|
};
|
|
|
|
export const lang = resolveLanguage();
|
|
|
|
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,
|
|
reqLang: data["Req-Lang"],
|
|
appVersion: appIntel.version,
|
|
buildVersion: appIntel.build,
|
|
channel: appIntel.channel,
|
|
reqImei: appIntel["Req-Imei"],
|
|
origin: sysOrigin.origin,
|
|
child: sysOrigin.child
|
|
};
|
|
}
|
|
|
|
export function setHeadersFromApp(rawOrObject) {
|
|
const data = typeof rawOrObject === "string" ? parseAppHeaders(rawOrObject) : rawOrObject || {};
|
|
const next = {};
|
|
if (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 };
|
|
}
|
|
|
|
export async function connectToApp() {
|
|
window.renderData = (raw) => raw && setHeadersFromApp(raw);
|
|
window.getIosAccessOriginParam = window.renderData;
|
|
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
|
const raw = window.app.getAccessOrigin();
|
|
if (raw) setHeadersFromApp(raw);
|
|
return;
|
|
}
|
|
if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") {
|
|
window.FlutterApp.postMessage("requestAccessOrigin");
|
|
}
|
|
setHeadersFromApp({ reqLang: lang, authorization: "" });
|
|
}
|
|
|
|
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() {
|
|
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", `
|
|
<header class="header">
|
|
<button class="icon-btn" type="button" data-action="back" aria-label="Back"><img src="../bd-static/icons/back.svg" alt=""></button>
|
|
<h1>${t(titleKey)}</h1>
|
|
<button class="lang-btn" type="button" data-action="language">EN</button>
|
|
</header>
|
|
`);
|
|
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");
|
|
});
|
|
}
|