(function () { const API_BASE = "https://jvapi.haiyihy.com"; const BACK_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEgAAABICAYAAABV7bNHAAAACXBIWXMAACE4AAAhOAFFljFgAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAGXSURBVHgB7dvBTcNAFIThB5xTQFwADVBBmqEnUgZQAAXAjRqgACgAj2wfECbJZXZnrPmkVRT5lF9ODn4vVRERERERETEaxnNb8cd+PA/jeZ3PY02xqG7KA+Ic6/eds5vfPxfRdelb4uxXrt0VmXqgU3Hgo8iUA52LA9SvF1yVpkviHOdDpRhIJg6oBZKKA0qB5OKASiDJOKAQSDYO9A4kHQd6BpKPA70CWcSBHoFs4kDrQFZxoGUguzjQKpBlHGgRyDYOsANZxwFmIPs4wAq0iTjACvRUG4gDjEeumDRsIg4wAn3VhjDmYt81jWOGf64vo5q3MsAaHOLDH2oa7q2xicQKhLvopTYQiTl63kQk9mzePlKL5QXrSK22O2wjtVx/sYzUej/ILlKPBSqrSL02zGwi9VzBs4jUe0dRPpLCEqd0JJUtV9lISmvAkpHU9qTlIikukktFUt20vzQSHu++F5HqGvDi3PgIgQ5FpL5p/zme+/l1za7IHP6rcSoSfdPeIRCsRcIPtM18rSUMJoeKiIiIiIjw9QNKh3OJhFQBoAAAAABJRU5ErkJggg=="; const DEFAULT_AVATAR = "../assets/defaultAvatar-CdxWBK1k.png"; const ENDPOINTS = { invitedList: "/team/bd/invite-recharge-agent-message-own", invite: "/team/bd/invite-recharge-agent", cancelPrefix: "/team/bd/invite-recharge-agent-cancel/", }; const TEXT = { pageTitle: "Invite User To Become Recharge Agent", language: "English", placeholder: "Enter User ID", search: "Search", invite: "Invite", idlePrompt: "please input user id", noUsersFound: "No users found", information: "Information:", userIdPrefix: "User ID:", pending: "Pending", success: "Success", cancel: "Cancel", invitationSubmitted: "Invitation submitted", applicationCancelled: "Application cancelled", somethingWentWrong: "Something went wrong", cancellationFailed: "Cancellation failed, try again", informationTitle: "Information", successTitle: "Success", errorTitle: "Error", ok: "OK", }; const state = { query: "", invitedList: [], searchResults: [], showSearchResults: false, searching: false, hasSearched: false, }; const dom = { statusBarPlaceholder: document.getElementById("statusBarPlaceholder"), backButton: document.getElementById("backButton"), backButtonIcon: document.getElementById("backButtonIcon"), pageTitle: document.getElementById("pageTitle"), languageEntry: document.getElementById("languageEntry"), languageName: document.getElementById("languageName"), searchInput: document.getElementById("searchInput"), clearButton: document.getElementById("clearButton"), searchButton: document.getElementById("searchButton"), resultList: document.getElementById("resultList"), noData: document.getElementById("noData"), userCardTemplate: document.getElementById("userCardTemplate"), }; init(); function init() { document.title = TEXT.pageTitle; document.documentElement.lang = "en"; document.documentElement.dir = resolveDocumentDirection(); dom.backButtonIcon.src = BACK_ICON; dom.pageTitle.textContent = TEXT.pageTitle; dom.languageName.textContent = TEXT.language; dom.searchInput.placeholder = TEXT.placeholder; dom.searchButton.textContent = TEXT.search; dom.noData.textContent = TEXT.idlePrompt; syncStatusBarPlaceholder(); bindEvents(); render(); loadInvitedList(); } function bindEvents() { dom.backButton.addEventListener("click", handleBack); dom.languageEntry.addEventListener("click", handleLanguageClick); dom.searchInput.addEventListener("input", handleSearchInput); dom.clearButton.addEventListener("click", clearSearch); dom.searchButton.addEventListener("click", triggerSearchNow); } function resolveDocumentDirection() { const lang = (new URLSearchParams(window.location.search).get("lang") || "").toLowerCase(); return lang.startsWith("ar") ? "rtl" : "ltr"; } function syncStatusBarPlaceholder() { const hasAppBridge = Boolean(window.app) || Boolean(window.FlutterPageControl) || Boolean(window.webkit && window.webkit.messageHandlers); const isIOS = /iphone|ipad|ipod/i.test(window.navigator.userAgent || ""); dom.statusBarPlaceholder.classList.toggle("is-visible", hasAppBridge && isIOS); } function handleBack() { if (window.history.length > 1) { window.history.back(); return; } window.location.href = "/"; } function handleLanguageClick() { window.location.href = "/language"; } function handleSearchInput(event) { state.query = event.target.value; dom.clearButton.hidden = !state.query; state.showSearchResults = false; state.hasSearched = false; render(); } function clearSearch() { state.query = ""; state.searchResults = []; state.showSearchResults = false; state.hasSearched = false; dom.searchInput.value = ""; dom.clearButton.hidden = true; render(); } async function triggerSearchNow() { if (state.searching) { return; } const query = state.query.trim(); if (!query) { state.showSearchResults = false; state.hasSearched = false; render(); return; } state.searching = true; state.showSearchResults = true; state.hasSearched = true; state.searchResults = []; render(); try { const payload = await searchUser(query); state.searchResults = payload ? [mergeWithInvitationStatus(payload.userProfile)] : []; } catch (error) { state.searchResults = []; showToast(extractErrorMessage(error), { title: TEXT.errorTitle, type: "error", }); } finally { state.searching = false; render(); } } async function loadInvitedList() { try { const response = await request("GET", ENDPOINTS.invitedList); state.invitedList = response && response.status && Array.isArray(response.body) ? response.body : []; state.searchResults = state.searchResults.map(function (item) { return mergeWithInvitationStatus(item.userProfile || {}); }); render(); } catch (error) { state.invitedList = []; render(); } } async function searchUser(query) { try { const response = await request( "GET", "/user/user-profile/open-search?account=" + encodeURIComponent(query) ); if (response && response.status && response.body) { return { userProfile: response.body }; } } catch (error) { if (!/^\d+$/.test(query)) { throw error; } } if (!/^\d+$/.test(query)) { return null; } const fallbackResponse = await request( "GET", "/user/user-profile?userId=" + encodeURIComponent(query) ); if (fallbackResponse && fallbackResponse.status && fallbackResponse.body) { return { userProfile: fallbackResponse.body }; } return null; } function mergeWithInvitationStatus(userProfile) { const matchedInvitation = state.invitedList.find(function (item) { return ( item && item.userProfile && userProfile && item.userProfile.id === userProfile.id ); }); return Object.assign({}, matchedInvitation || {}, { userProfile: userProfile || {}, }); } async function inviteUser(item) { if (!item || !item.userProfile || !item.userProfile.id) { return; } try { const response = await request("POST", ENDPOINTS.invite, { body: { inviteUserId: item.userProfile.id, }, }); if (response && response.status) { showToast(TEXT.invitationSubmitted, { title: TEXT.successTitle, type: "success", }); await loadInvitedList(); } } catch (error) { showToast(extractErrorMessage(error, TEXT.somethingWentWrong), { title: TEXT.errorTitle, type: "error", }); } } async function cancelInvitation(invitationId) { try { const response = await request("POST", ENDPOINTS.cancelPrefix + invitationId); if (response && response.status) { showToast(TEXT.applicationCancelled, { title: TEXT.informationTitle, type: "info", }); await loadInvitedList(); } } catch (error) { showToast(extractErrorMessage(error, TEXT.cancellationFailed), { title: TEXT.errorTitle, type: "error", }); } } function render() { renderSearchButton(); renderList(); } function renderSearchButton() { dom.searchButton.disabled = !state.query.trim() || state.searching; } function renderList() { const currentList = state.showSearchResults ? state.searchResults : state.invitedList; dom.resultList.innerHTML = ""; currentList.forEach(function (item) { dom.resultList.appendChild(createUserCard(item, state.showSearchResults ? "search" : "invited")); }); if (currentList.length > 0 || (state.searching && state.showSearchResults)) { dom.noData.hidden = true; return; } dom.noData.hidden = false; dom.noData.textContent = state.showSearchResults && state.hasSearched ? TEXT.noUsersFound : TEXT.idlePrompt; } function createUserCard(item, mode) { const fragment = dom.userCardTemplate.content.cloneNode(true); const statusBadge = fragment.querySelector(".status-badge"); const avatarImage = fragment.querySelector(".avatar-image"); const nickname = fragment.querySelector(".nickname"); const account = fragment.querySelector(".account"); const pendingActions = fragment.querySelector(".pending-actions"); const userProfile = item.userProfile || {}; avatarImage.src = userProfile.userAvatar || DEFAULT_AVATAR; avatarImage.alt = userProfile.userNickname || ""; avatarImage.onerror = handleAvatarError; nickname.textContent = userProfile.userNickname || ""; account.textContent = TEXT.userIdPrefix + " " + (userProfile.account || userProfile.id || ""); if (item.status === 0) { statusBadge.innerHTML = '
' + TEXT.pending + "
"; } else if (item.status === 1) { statusBadge.innerHTML = '
' + TEXT.success + "
"; } if (mode === "search") { const inviteButton = document.createElement("button"); inviteButton.type = "button"; inviteButton.className = "result-action-btn"; inviteButton.textContent = TEXT.invite; inviteButton.disabled = item.status === 0 || item.status === 1; inviteButton.addEventListener("click", function (event) { event.stopPropagation(); inviteUser(item); }); pendingActions.appendChild(inviteButton); } else if (item.status === 0) { const cancelButton = document.createElement("div"); cancelButton.textContent = TEXT.cancel; cancelButton.style.padding = "4px 8px"; cancelButton.style.borderRadius = "32px"; cancelButton.style.backgroundColor = "red"; cancelButton.style.color = "white"; cancelButton.style.fontWeight = "600"; cancelButton.style.zIndex = "5"; cancelButton.addEventListener("click", function (event) { event.stopPropagation(); cancelInvitation(item.id); }); pendingActions.appendChild(cancelButton); } return fragment; } function handleAvatarError(event) { event.target.onerror = null; event.target.src = DEFAULT_AVATAR; } function extractErrorMessage(error, fallback) { return ( (error && error.response && (error.response.errorMsg || error.response.message || error.response.error)) || (error && error.message) || fallback || TEXT.somethingWentWrong ); } function showToast(message, options) { const config = Object.assign( { title: TEXT.informationTitle, type: "info", confirmText: TEXT.ok, }, options || {} ); const iconMap = { error: "✕", warning: "⚠", success: "✓", info: "i", simple: "", }; const overlay = document.createElement("div"); overlay.className = "modal-overlay"; overlay.innerHTML = '"; const modal = overlay.querySelector(".modal"); const closeButton = overlay.querySelector(".modal-btn"); const close = function () { modal.classList.remove("modal-enter"); modal.classList.add("modal-leave"); window.setTimeout(function () { overlay.remove(); }, 200); }; closeButton.addEventListener("click", close); overlay.addEventListener("click", function (event) { if (event.target === overlay) { close(); } }); document.body.appendChild(overlay); } function escapeHtml(value) { return String(value) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } async function request(method, path, options) { const config = options || {}; const headers = buildRequestHeaders(Boolean(config.body), config.headers || {}); const response = await fetch(API_BASE + path, { method: method, credentials: "include", headers: headers, body: config.body ? JSON.stringify(config.body) : undefined, }); const text = await response.text(); let payload = {}; if (text) { try { payload = JSON.parse(text); } catch (error) { payload = { status: response.ok, body: text, }; } } if (!response.ok || payload.status === false) { const requestError = new Error( payload.errorMsg || payload.message || response.statusText || TEXT.somethingWentWrong ); requestError.response = payload; throw requestError; } return payload; } function buildRequestHeaders(hasBody, overrideHeaders) { const discovered = discoverAppHeaders(); const headers = Object.assign({}, overrideHeaders || {}); if (hasBody) { headers["Content-Type"] = headers["Content-Type"] || "application/json"; } headers.Accept = headers.Accept || "application/json, text/plain, */*"; if (discovered.authorization) { headers.Authorization = headers.Authorization || discovered.authorization; } if (discovered.reqLang) { headers["Req-Lang"] = headers["Req-Lang"] || discovered.reqLang; } if (discovered.appVersion) { headers["App-Version"] = headers["App-Version"] || discovered.appVersion; } if (discovered.buildVersion) { headers["Build-Version"] = headers["Build-Version"] || discovered.buildVersion; } if (discovered.appChannel) { headers["App-Channel"] = headers["App-Channel"] || discovered.appChannel; } if (discovered.reqImei) { headers["Req-Imei"] = headers["Req-Imei"] || discovered.reqImei; } if (discovered.sysOrigin) { headers["Sys-Origin"] = headers["Sys-Origin"] || discovered.sysOrigin; } if (discovered.sysOriginChild) { headers["Sys-Origin-Child"] = headers["Sys-Origin-Child"] || discovered.sysOriginChild; } return headers; } function discoverAppHeaders() { const query = new URLSearchParams(window.location.search); const collected = []; collectCandidate(collected, window.__APP_HEADERS__); collectCandidate(collected, window.__HEADER_INFO__); collectCandidate(collected, window.headerInfo); collectCandidate(collected, parseHeaderObjectFromQuery(query)); collectFromStorage(collected, window.localStorage); collectFromStorage(collected, window.sessionStorage); return collected.reduce(function (result, candidate) { return Object.assign(result, candidate); }, { reqLang: query.get("lang") || document.documentElement.lang || "en", }); } function parseHeaderObjectFromQuery(query) { const candidate = {}; mapHeaderValue(candidate, "authorization", query.get("authorization")); mapHeaderValue(candidate, "authorization", query.get("token")); mapHeaderValue(candidate, "reqLang", query.get("lang")); mapHeaderValue(candidate, "appVersion", query.get("appVersion")); mapHeaderValue(candidate, "buildVersion", query.get("buildVersion")); mapHeaderValue(candidate, "appChannel", query.get("appChannel")); mapHeaderValue(candidate, "reqImei", query.get("reqImei")); mapHeaderValue(candidate, "sysOrigin", query.get("sysOrigin")); mapHeaderValue(candidate, "sysOriginChild", query.get("sysOriginChild")); return candidate; } function collectFromStorage(collected, storage) { if (!storage) { return; } const preferredKeys = [ "authorization", "Authorization", "token", "accessToken", "headerInfo", "headersFromApp", "appHeaders", "persist:root", "user", "userInfo", ]; preferredKeys.forEach(function (key) { try { collectCandidate(collected, storage.getItem(key)); } catch (error) { return; } }); for (let index = 0; index < storage.length; index += 1) { try { const key = storage.key(index); collectCandidate(collected, storage.getItem(key)); } catch (error) { return; } } } function collectCandidate(collected, value) { const normalized = normalizeHeaderCandidate(value, 0); if (normalized && Object.keys(normalized).length > 0) { collected.push(normalized); } } function normalizeHeaderCandidate(value, depth) { if (depth > 4 || value == null) { return null; } if (typeof value === "string") { const trimmed = value.trim(); if (!trimmed) { return null; } if (trimmed[0] === "{" || trimmed[0] === "[") { try { return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1); } catch (error) { return null; } } if (/bearer\\s+/i.test(trimmed)) { return { authorization: trimmed, }; } return null; } if (Array.isArray(value)) { return value.reduce(function (result, entry) { const normalized = normalizeHeaderCandidate(entry, depth + 1); return normalized ? Object.assign(result, normalized) : result; }, {}); } if (typeof value !== "object") { return null; } const normalized = {}; const aliasMap = { authorization: ["authorization", "Authorization", "token", "accessToken"], reqLang: ["reqLang", "Req-Lang", "lang", "language"], appVersion: ["appVersion", "App-Version"], buildVersion: ["buildVersion", "Build-Version"], appChannel: ["appChannel", "App-Channel"], reqImei: ["reqImei", "Req-Imei"], sysOrigin: ["sysOrigin", "Sys-Origin"], sysOriginChild: ["sysOriginChild", "Sys-Origin-Child"], }; Object.keys(aliasMap).forEach(function (targetKey) { aliasMap[targetKey].forEach(function (sourceKey) { mapHeaderValue(normalized, targetKey, value[sourceKey]); }); }); ["headerInfo", "headers", "auth", "data", "state"].forEach(function (nestedKey) { const nestedValue = normalizeHeaderCandidate(value[nestedKey], depth + 1); if (nestedValue) { Object.assign(normalized, nestedValue); } }); Object.keys(value).forEach(function (key) { if ( key === "headerInfo" || key === "headers" || key === "auth" || key === "data" || key === "state" ) { return; } if (typeof value[key] === "object") { const nestedValue = normalizeHeaderCandidate(value[key], depth + 1); if (nestedValue) { Object.assign(normalized, nestedValue); } } }); return normalized; } function mapHeaderValue(target, key, value) { if (value == null || value === "" || target[key]) { return; } target[key] = String(value); } })();