(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-bdleader-message-own", invite: "/team/bd/invite-bdleader", cancelPrefix: "/team/bd/invite-bdleader-cancel/", }; const TEXT = { pageTitle: "Invite User To Become BD Leader", 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 = '