566 lines
18 KiB
JavaScript
566 lines
18 KiB
JavaScript
(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: "Join Agency",
|
||
page_label: "Join Agency",
|
||
back: "Back",
|
||
title: "Join Agency",
|
||
language_button_aria: "Change language",
|
||
agency_id: "Agency ID",
|
||
enter_agent_id: "Enter agency ID",
|
||
search: "Search",
|
||
searching: "Searching...",
|
||
apply: "Apply",
|
||
applying: "Applying...",
|
||
agency: "Agency",
|
||
uid_prefix: "UID:",
|
||
account_not_found: "Account not found or not an agency",
|
||
missing_token: "Missing token",
|
||
found: "Agency found",
|
||
application_submitted: "Application submitted",
|
||
application_failed: "Application failed",
|
||
application_records: "Application Records",
|
||
pending: "Pending",
|
||
cancel: "Cancel",
|
||
cancelling: "Cancelling...",
|
||
cancellation_failed: "Cancellation failed",
|
||
application_cancelled: "Application cancelled",
|
||
network_error: "Network error"
|
||
},
|
||
ar: {
|
||
document_title: "الانضمام إلى وكالة",
|
||
page_label: "الانضمام إلى وكالة",
|
||
back: "رجوع",
|
||
title: "الانضمام إلى وكالة",
|
||
language_button_aria: "تغيير اللغة",
|
||
agency_id: "معرّف الوكالة",
|
||
enter_agent_id: "أدخل معرّف الوكالة",
|
||
search: "بحث",
|
||
searching: "جار البحث...",
|
||
apply: "تقديم",
|
||
applying: "جار التقديم...",
|
||
agency: "الوكالة",
|
||
uid_prefix: "UID:",
|
||
account_not_found: "لم يتم العثور على وكالة",
|
||
missing_token: "رمز الدخول مفقود",
|
||
found: "تم العثور على الوكالة",
|
||
application_submitted: "تم إرسال الطلب",
|
||
application_failed: "فشل الطلب",
|
||
application_records: "سجلات الطلب",
|
||
pending: "قيد الانتظار",
|
||
cancel: "إلغاء",
|
||
cancelling: "جار الإلغاء...",
|
||
cancellation_failed: "فشل الإلغاء",
|
||
application_cancelled: "تم إلغاء الطلب",
|
||
network_error: "خطأ في الشبكة"
|
||
},
|
||
tr: {
|
||
document_title: "Ajansa Katıl",
|
||
page_label: "Ajansa Katıl",
|
||
back: "Geri",
|
||
title: "Ajansa Katıl",
|
||
language_button_aria: "Dili değiştir",
|
||
agency_id: "Ajans ID",
|
||
enter_agent_id: "Ajans ID gir",
|
||
search: "Ara",
|
||
searching: "Aranıyor...",
|
||
apply: "Başvur",
|
||
applying: "Başvuruluyor...",
|
||
agency: "Ajans",
|
||
uid_prefix: "UID:",
|
||
account_not_found: "Hesap bulunamadı veya ajans değil",
|
||
missing_token: "Token eksik",
|
||
found: "Ajans bulundu",
|
||
application_submitted: "Başvuru gönderildi",
|
||
application_failed: "Başvuru başarısız",
|
||
application_records: "Başvuru Kayıtları",
|
||
pending: "Beklemede",
|
||
cancel: "İptal",
|
||
cancelling: "İptal ediliyor...",
|
||
cancellation_failed: "İptal başarısız",
|
||
application_cancelled: "Başvuru iptal edildi",
|
||
network_error: "Ağ hatası"
|
||
},
|
||
id: {
|
||
document_title: "Gabung Agensi",
|
||
page_label: "Gabung Agensi",
|
||
back: "Kembali",
|
||
title: "Gabung Agensi",
|
||
language_button_aria: "Ubah bahasa",
|
||
agency_id: "ID Agensi",
|
||
enter_agent_id: "Masukkan ID agensi",
|
||
search: "Cari",
|
||
searching: "Mencari...",
|
||
apply: "Ajukan",
|
||
applying: "Mengajukan...",
|
||
agency: "Agensi",
|
||
uid_prefix: "UID:",
|
||
account_not_found: "Akun tidak ditemukan atau bukan agensi",
|
||
missing_token: "Token tidak ditemukan",
|
||
found: "Agensi ditemukan",
|
||
application_submitted: "Pengajuan dikirim",
|
||
application_failed: "Pengajuan gagal",
|
||
application_records: "Riwayat Pengajuan",
|
||
pending: "Menunggu",
|
||
cancel: "Batal",
|
||
cancelling: "Membatalkan...",
|
||
cancellation_failed: "Gagal membatalkan",
|
||
application_cancelled: "Pengajuan dibatalkan",
|
||
network_error: "Kesalahan jaringan"
|
||
}
|
||
};
|
||
|
||
const params = readURLParams();
|
||
let currentLang = normalizeLanguage(params.get("lang"));
|
||
let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en;
|
||
let foundAgency = null;
|
||
let pendingRecord = null;
|
||
|
||
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) {
|
||
return currentMessages[key] || fallbackMessages.en[key] || 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 apiRequest(path, options = {}) {
|
||
const token = readRawParam("token");
|
||
if (!token) throw new Error(t("missing_token"));
|
||
|
||
const method = options.method || "GET";
|
||
const headers = authHeadersFromToken(token);
|
||
const request = {
|
||
method,
|
||
cache: "no-store",
|
||
headers
|
||
};
|
||
|
||
if (options.body) {
|
||
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;
|
||
}
|
||
|
||
async function loadMessages(lang) {
|
||
return fallbackMessages[normalizeLanguage(lang)] || fallbackMessages.en;
|
||
}
|
||
|
||
function renderMessages(messages) {
|
||
document.querySelectorAll("[data-i18n]").forEach((node) => {
|
||
const value = messages[node.dataset.i18n];
|
||
if (value) node.textContent = value;
|
||
});
|
||
|
||
document.querySelectorAll("[data-i18n-aria]").forEach((node) => {
|
||
const value = messages[node.dataset.i18nAria];
|
||
if (value) node.setAttribute("aria-label", value);
|
||
});
|
||
|
||
document.querySelectorAll("[data-i18n-placeholder]").forEach((node) => {
|
||
const value = messages[node.dataset.i18nPlaceholder];
|
||
if (value) node.setAttribute("placeholder", value);
|
||
});
|
||
}
|
||
|
||
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 renderLanguageControls(messages) {
|
||
const languageButton = document.querySelector(".language-button");
|
||
if (languageButton) {
|
||
languageButton.textContent = languageLabels[currentLang];
|
||
languageButton.setAttribute("aria-label", messages.language_button_aria || "Change language");
|
||
}
|
||
|
||
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));
|
||
});
|
||
}
|
||
|
||
async function applyLanguage(lang) {
|
||
currentLang = normalizeLanguage(lang);
|
||
const messages = await loadMessages(currentLang);
|
||
currentMessages = messages;
|
||
|
||
document.documentElement.lang = currentLang;
|
||
document.documentElement.dir = currentLang === "ar" ? "rtl" : "ltr";
|
||
document.title = messages.document_title || "Join Agency";
|
||
renderMessages(messages);
|
||
renderLanguageControls(messages);
|
||
renderFoundAgency(foundAgency);
|
||
renderPendingRecord(pendingRecord);
|
||
}
|
||
|
||
function showMessage(message, type = "") {
|
||
const node = document.querySelector("#statusMessage");
|
||
if (!node) return;
|
||
|
||
node.textContent = message || "";
|
||
node.hidden = !message;
|
||
node.classList.toggle("is-error", type === "error");
|
||
node.classList.toggle("is-success", type === "success");
|
||
}
|
||
|
||
function pickProfile(body) {
|
||
return body?.ownUserProfile || body?.userProfile || body?.teamProfile || body || null;
|
||
}
|
||
|
||
function pickSpecialAccount(profile) {
|
||
return profile?.ownSpecialId?.account ||
|
||
profile?.specialId?.account ||
|
||
profile?.specialAccount ||
|
||
profile?.shortAccount ||
|
||
profile?.shortId ||
|
||
"";
|
||
}
|
||
|
||
function pickSearchAgency(body) {
|
||
const teamProfile = body?.teamProfile || null;
|
||
const userProfile = body?.ownUserProfile || body?.userProfile || teamProfile?.ownUserProfile || teamProfile?.userProfile || null;
|
||
if (!teamProfile?.id) return null;
|
||
|
||
const specialAccount = pickSpecialAccount(userProfile) || pickSpecialAccount(teamProfile);
|
||
|
||
return {
|
||
...(teamProfile || {}),
|
||
...(userProfile || {}),
|
||
id: teamProfile.id,
|
||
teamId: teamProfile.id,
|
||
teamName: teamProfile.teamName || userProfile?.teamName || "",
|
||
userNickname: userProfile?.userNickname || teamProfile.userNickname || teamProfile.name || teamProfile.teamName || "",
|
||
name: userProfile?.name || teamProfile.name || teamProfile.teamName || "",
|
||
userAvatar: userProfile?.userAvatar || teamProfile.userAvatar || userProfile?.avatar || teamProfile.avatar || "",
|
||
avatar: userProfile?.avatar || teamProfile.avatar || userProfile?.userAvatar || teamProfile.userAvatar || "",
|
||
displayAccount: specialAccount || userProfile?.account || teamProfile.account || "",
|
||
account: userProfile?.account || teamProfile.account || "",
|
||
ownSpecialId: userProfile?.ownSpecialId || teamProfile.ownSpecialId || (specialAccount ? { account: specialAccount } : null),
|
||
specialId: userProfile?.specialId || teamProfile.specialId || null,
|
||
specialAccount,
|
||
shortAccount: userProfile?.shortAccount || teamProfile.shortAccount || "",
|
||
shortId: userProfile?.shortId || teamProfile.shortId || ""
|
||
};
|
||
}
|
||
|
||
function pickName(profile) {
|
||
return profile?.userNickname || profile?.name || profile?.teamName || "-";
|
||
}
|
||
|
||
function pickAccount(profile) {
|
||
return pickSpecialAccount(profile) || profile?.displayAccount || profile?.account || profile?.id || "-";
|
||
}
|
||
|
||
function initials(name) {
|
||
const value = String(name || "A").trim();
|
||
return value ? value.slice(0, 1).toUpperCase() : "A";
|
||
}
|
||
|
||
function renderAvatar(profile, imageSelector, fallbackSelector) {
|
||
const image = document.querySelector(imageSelector);
|
||
const fallback = document.querySelector(fallbackSelector);
|
||
if (!image || !fallback) return;
|
||
|
||
const name = pickName(profile);
|
||
const avatar = profile?.userAvatar || profile?.avatar || "";
|
||
fallback.textContent = initials(name);
|
||
image.alt = name;
|
||
|
||
if (!avatar) {
|
||
image.hidden = true;
|
||
image.removeAttribute("src");
|
||
fallback.hidden = false;
|
||
return;
|
||
}
|
||
|
||
fallback.hidden = true;
|
||
image.hidden = false;
|
||
image.onerror = () => {
|
||
image.hidden = true;
|
||
image.removeAttribute("src");
|
||
fallback.hidden = false;
|
||
};
|
||
image.src = avatar;
|
||
}
|
||
|
||
function syncApplyAvailability() {
|
||
const button = document.querySelector("#applyButton");
|
||
if (button) button.hidden = Boolean(pendingRecord);
|
||
}
|
||
|
||
function renderFoundAgency(profile) {
|
||
const card = document.querySelector("#resultCard");
|
||
if (!card) return;
|
||
|
||
if (!profile) {
|
||
card.hidden = true;
|
||
syncApplyAvailability();
|
||
return;
|
||
}
|
||
|
||
card.hidden = false;
|
||
document.querySelector("#agencyName").textContent = pickName(profile);
|
||
document.querySelector("#agencyAccountText").textContent = `${t("uid_prefix")} ${pickAccount(profile)}`.trim();
|
||
renderAvatar(profile, "#agencyAvatar", "#agencyAvatarFallback");
|
||
syncApplyAvailability();
|
||
}
|
||
|
||
function renderPendingRecord(record) {
|
||
const card = document.querySelector("#pendingCard");
|
||
if (!card) return;
|
||
|
||
if (!record) {
|
||
card.hidden = true;
|
||
syncApplyAvailability();
|
||
return;
|
||
}
|
||
|
||
const profile = pickProfile(record);
|
||
card.hidden = false;
|
||
document.querySelector("#pendingName").textContent = pickName(profile);
|
||
document.querySelector("#pendingAccount").textContent = `${t("uid_prefix")} ${pickAccount(profile)}`.trim();
|
||
renderAvatar(profile, "#pendingAvatar", "#pendingAvatarFallback");
|
||
syncApplyAvailability();
|
||
}
|
||
|
||
function errorMessage(error, fallbackKey) {
|
||
return error?.response?.errorMsg || error?.response?.message || error?.message || t(fallbackKey);
|
||
}
|
||
|
||
async function searchAgency() {
|
||
const input = document.querySelector("#agencyAccount");
|
||
const button = document.querySelector("#searchButton");
|
||
const account = input?.value.trim() || "";
|
||
|
||
if (!account) {
|
||
showMessage(t("enter_agent_id"), "error");
|
||
return;
|
||
}
|
||
|
||
button.disabled = true;
|
||
button.textContent = t("searching");
|
||
showMessage("");
|
||
|
||
try {
|
||
const body = await apiRequest(`/team/search-account?account=${encodeURIComponent(account)}`);
|
||
const profile = pickSearchAgency(body);
|
||
if (!profile?.id) {
|
||
foundAgency = null;
|
||
renderFoundAgency(null);
|
||
showMessage(t("account_not_found"), "error");
|
||
return;
|
||
}
|
||
|
||
foundAgency = profile;
|
||
renderFoundAgency(foundAgency);
|
||
showMessage(t("found"), "success");
|
||
} catch (error) {
|
||
foundAgency = null;
|
||
renderFoundAgency(null);
|
||
showMessage(errorMessage(error, "network_error"), "error");
|
||
} finally {
|
||
button.disabled = false;
|
||
button.textContent = t("search");
|
||
}
|
||
}
|
||
|
||
async function fetchPendingRecord() {
|
||
try {
|
||
pendingRecord = await apiRequest("/team/wait-apply/profile");
|
||
renderPendingRecord(pendingRecord);
|
||
} catch (error) {
|
||
pendingRecord = null;
|
||
renderPendingRecord(null);
|
||
if (error.message === t("missing_token")) showMessage(error.message, "error");
|
||
}
|
||
}
|
||
|
||
async function applyToAgency() {
|
||
if (pendingRecord) return;
|
||
if (!foundAgency?.id) return;
|
||
|
||
const button = document.querySelector("#applyButton");
|
||
button.disabled = true;
|
||
button.textContent = t("applying");
|
||
showMessage("");
|
||
|
||
try {
|
||
await apiRequest("/team/user/apply", {
|
||
method: "POST",
|
||
body: {
|
||
teamId: foundAgency.id,
|
||
reason: "JOIN"
|
||
}
|
||
});
|
||
foundAgency = null;
|
||
renderFoundAgency(null);
|
||
showMessage(t("application_submitted"), "success");
|
||
await fetchPendingRecord();
|
||
} catch (error) {
|
||
showMessage(errorMessage(error, "application_failed"), "error");
|
||
} finally {
|
||
button.disabled = false;
|
||
button.textContent = t("apply");
|
||
}
|
||
}
|
||
|
||
async function cancelApplication() {
|
||
const id = pendingRecord?.messageId || pendingRecord?.id;
|
||
if (!id) return;
|
||
|
||
const button = document.querySelector("#cancelButton");
|
||
button.disabled = true;
|
||
button.textContent = t("cancelling");
|
||
showMessage("");
|
||
|
||
try {
|
||
await apiRequest("/team/user/join-apply-cancel", {
|
||
method: "POST",
|
||
body: { id }
|
||
});
|
||
pendingRecord = null;
|
||
renderPendingRecord(null);
|
||
showMessage(t("application_cancelled"), "success");
|
||
} catch (error) {
|
||
showMessage(errorMessage(error, "cancellation_failed"), "error");
|
||
} finally {
|
||
button.disabled = false;
|
||
button.textContent = t("cancel");
|
||
}
|
||
}
|
||
|
||
function buildPageURL(path) {
|
||
const url = new URL(path, window.location.href);
|
||
const nextParams = new URLSearchParams(params);
|
||
const token = readRawParam("token");
|
||
if (token) nextParams.set("token", token);
|
||
if (currentLang) nextParams.set("lang", currentLang);
|
||
url.search = nextParams.toString();
|
||
return url.toString();
|
||
}
|
||
|
||
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.addEventListener("click", () => {
|
||
setLanguageMenuOpen(false);
|
||
});
|
||
|
||
document.querySelector(".back-button")?.addEventListener("click", () => {
|
||
if (window.history.length > 1) {
|
||
window.history.back();
|
||
return;
|
||
}
|
||
window.location.href = buildPageURL("../host-center/index.html");
|
||
});
|
||
|
||
document.querySelector("#searchForm")?.addEventListener("submit", (event) => {
|
||
event.preventDefault();
|
||
searchAgency();
|
||
});
|
||
|
||
document.querySelector("#applyButton")?.addEventListener("click", applyToAgency);
|
||
document.querySelector("#cancelButton")?.addEventListener("click", cancelApplication);
|
||
|
||
applyLanguage(currentLang).then(fetchPendingRecord);
|
||
})();
|