diff --git a/h5/hyapp/manager-center/index.html b/h5/hyapp/manager-center/index.html index c3b2f73..7b3f2a6 100644 --- a/h5/hyapp/manager-center/index.html +++ b/h5/hyapp/manager-center/index.html @@ -4,7 +4,7 @@ Manager Center - +
@@ -39,35 +39,8 @@
-
-
-

User

-
-
- - -
- -
- - - - - -
-
-
-

BD Leaders

-
0
-
- -
- -
-
+ - - + diff --git a/h5/hyapp/manager-center/script.js b/h5/hyapp/manager-center/script.js index 0358942..a34fe4c 100644 --- a/h5/hyapp/manager-center/script.js +++ b/h5/hyapp/manager-center/script.js @@ -349,6 +349,7 @@ const state = { manager: null, target: null, + activeAction: "", features: defaultFeatures(), propsType: defaultPropsType, props: [], @@ -363,6 +364,54 @@ addingBDLeader: false }; + const actionConfigs = { + addBDLeader: { + titleKey: "add_bd_leader", + disabledKey: "add_bd_leader_disabled", + feature: "addBdLeader", + panel: "#addBDLeaderPanel", + status: "#addBDLeaderStatus", + needsUser: true + }, + changeCountry: { + titleKey: "change_country", + disabledKey: "change_country_disabled", + feature: "changeCountry", + panel: "#countryPanel", + status: "#countryStatus", + needsUser: true + }, + giftProps: { + titleKey: "gift_props", + disabledKey: "gift_props_disabled", + feature: "giftProps", + panel: "#giftPanel", + status: "#propsStatus", + needsUser: true + }, + banUser: { + titleKey: "ban_user", + disabledKey: "ban_user_disabled", + feature: "banUser", + panel: "#banPanel", + status: "#banStatus", + needsUser: true + }, + unbanUser: { + titleKey: "unban_user", + disabledKey: "unban_user_disabled", + feature: "unbanUser", + panel: "#unbanPanel", + status: "#unbanStatus", + needsUser: true + }, + bdLeaders: { + titleKey: "bd_leaders", + panel: "#bdLeaderPanel", + needsUser: false + } + }; + const params = readURLParams(); let currentLang = normalizeLanguage(params.get("lang")); let currentMessages = fallbackMessages[currentLang] || fallbackMessages.en; @@ -414,7 +463,7 @@ if (window.location.protocol === "file:") return fallbackMessages[normalizedLang]; try { - const response = await fetch(`./locales/${normalizedLang}.json?v=20260624-0100`, { cache: "no-store" }); + const response = await fetch(`./locales/${normalizedLang}.json?v=20260625-0100`, { cache: "no-store" }); if (response.ok) return await response.json(); } catch (error) { return fallbackMessages[normalizedLang]; @@ -570,6 +619,7 @@ renderCountrySelect(); renderManager(state.manager); renderTarget(); + renderActionModal(); renderProps(); renderBDLeaders(); updateActionState(); @@ -723,6 +773,47 @@ document.body.classList.toggle("is-modal-open", Boolean(document.querySelector(".modal-mask:not([hidden])"))); } + function activeConfig() { + return actionConfigs[state.activeAction] || null; + } + + function clearActionStatuses() { + setStatus($("#modalSearchStatus"), ""); + Object.values(actionConfigs).forEach((config) => { + if (config.status) setStatus($(config.status), ""); + }); + } + + function clearActionModalForm() { + const input = $("#modalAccountInput"); + if (input) input.value = ""; + const banReason = $("#banReason"); + const unbanReason = $("#unbanReason"); + if (banReason) banReason.value = ""; + if (unbanReason) unbanReason.value = ""; + state.target = null; + state.selectedCountryId = ""; + renderTarget(); + clearActionStatuses(); + } + + function renderActionModal() { + const config = activeConfig(); + const title = $("#actionModalTitle"); + if (title) title.textContent = config ? message(config.titleKey) : "-"; + + const needsUser = config?.needsUser !== false; + const searchForm = $("#modalSearchForm"); + const modalTarget = $("#targetCard"); + if (searchForm) searchForm.hidden = !needsUser; + if (modalTarget && !needsUser) modalTarget.hidden = true; + + Object.values(actionConfigs).forEach((item) => { + const panel = $(item.panel); + if (panel) panel.hidden = item !== config || (item.needsUser !== false && !state.target); + }); + } + function setModalOpen(selector, open) { const modal = $(selector); if (!modal) return; @@ -739,77 +830,56 @@ document.querySelectorAll(".modal-mask").forEach((modal) => { modal.hidden = true; }); + state.activeAction = ""; + state.target = null; setPropsTypeMenuOpen(false); setCountryMenuOpen(false); + renderTarget(); syncModalState(); } - function openGiftModal() { - if (!featureEnabled("giftProps")) { - showToast(message("gift_props_disabled")); + function openActionModal(action) { + const config = actionConfigs[action]; + if (!config) return; + if (config.feature && !featureEnabled(config.feature)) { + showToast(message(config.disabledKey)); return; } - if (!state.target) { - showToast(message("enter_user")); - return; - } - setModalOpen("#giftModal", true); + + state.activeAction = action; + clearActionModalForm(); + renderActionModal(); + setModalOpen("#actionModal", true); updateActionState(); + + if (action === "giftProps" && !state.props.length) { + loadProps().catch((error) => { + setStatus($("#propsStatus"), friendlyError(error), "error"); + }); + } + if (action === "bdLeaders") { + renderBDLeaders(); + } + + window.setTimeout(() => { + if (config.needsUser !== false) $("#modalAccountInput")?.focus(); + }, 0); } - async function openCountryModal() { - if (!featureEnabled("changeCountry")) { - showToast(message("change_country_disabled")); - return; - } - if (!state.target) { - showToast(message("enter_user")); - return; - } + function openGiftModal() { + openActionModal("giftProps"); + } - setStatus($("#countryStatus"), ""); - const currentCountryText = $("#currentCountryText"); - if (currentCountryText) currentCountryText.textContent = `${message("current_country")}: ${displayCountryText(state.target)}`; - state.selectedCountryId = String(state.target.countryId || matchingCountryId(state.target) || ""); - setModalOpen("#countryModal", true); - renderCountrySelect(); - updateActionState(); - - if (!state.countries.length) { - try { - await loadCountries(); - } catch (error) { - setStatus($("#countryStatus"), friendlyError(error), "error"); - } - } + function openCountryModal() { + openActionModal("changeCountry"); } function openBanModal() { - if (!featureEnabled("banUser")) { - showToast(message("ban_user_disabled")); - return; - } - if (!state.target) { - showToast(message("enter_user")); - return; - } - setStatus($("#banStatus"), ""); - setModalOpen("#banModal", true); - updateActionState(); + openActionModal("banUser"); } function openUnbanModal() { - if (!featureEnabled("unbanUser")) { - showToast(message("unban_user_disabled")); - return; - } - if (!state.target) { - showToast(message("enter_user")); - return; - } - setStatus($("#unbanStatus"), ""); - setModalOpen("#unbanModal", true); - updateActionState(); + openActionModal("unbanUser"); } function closePage() { @@ -943,6 +1013,13 @@ return message("bd_leader_count", "{count} BD Leaders").replace("{count}", new Intl.NumberFormat("en-US").format(Number(count) || 0)); } + function formatBDLeaderSummary() { + const countText = formatBDLeaderCount(state.bdLeaders.length); + const firstLeader = state.bdLeaders[0]; + if (!firstLeader) return countText; + return `${countText} - ${userIDText(firstLeader)}`; + } + function formatDateTime(value) { if (!value) return ""; const date = new Date(value); @@ -973,18 +1050,15 @@ function renderTarget() { const card = $("#targetCard"); - const actionCard = $("#actionCard"); if (!state.target) { if (card) card.hidden = true; - if (actionCard) actionCard.hidden = true; - closeModals(); + renderActionModal(); updateActionState(); return; } const target = state.target; if (card) card.hidden = false; - if (actionCard) actionCard.hidden = false; $("#targetName").textContent = displayName(target); $("#targetUid").textContent = userIDText(target); $("#targetAccountStatus").textContent = target.accountStatus || "-"; @@ -1002,6 +1076,28 @@ unbanEligibility.classList.toggle("is-danger", !canUnban); } renderAvatar("#targetAvatar", "#targetAvatarFallback", target.userAvatar, displayName(target), "U"); + renderActionModal(); + updateActionState(); + } + + async function prepareActiveActionAfterTarget() { + if (!state.target) return; + + if (state.activeAction === "changeCountry") { + const currentCountryText = $("#currentCountryText"); + if (currentCountryText) currentCountryText.textContent = `${message("current_country")}: ${displayCountryText(state.target)}`; + state.selectedCountryId = String(state.target.countryId || matchingCountryId(state.target) || ""); + renderCountrySelect(); + if (!state.countries.length) { + await loadCountries(); + } + return; + } + + if (state.activeAction === "giftProps") { + renderProps(); + } + updateActionState(); } @@ -1071,7 +1167,10 @@ function renderBDLeaders() { const list = $("#bdLeaderList"); const count = $("#bdLeaderCount"); - if (count) count.textContent = formatBDLeaderCount(state.bdLeaders.length); + const summary = $("#bdLeaderSummary"); + const leaderCountText = formatBDLeaderCount(state.bdLeaders.length); + if (count) count.textContent = leaderCountText; + if (summary) summary.textContent = formatBDLeaderSummary(); if (!list) return; list.replaceChildren(); @@ -1116,10 +1215,7 @@ name.textContent = displayName(leader); const meta = document.createElement("span"); meta.className = "bd-leader-meta"; - const metaItems = [userIDText(leader)]; - if (leader.regionId) metaItems.push(leader.regionId); - if (leader.originSys) metaItems.push(leader.originSys); - meta.textContent = metaItems.join(" ยท "); + meta.textContent = userIDText(leader); copy.append(name, meta); const time = document.createElement("span"); @@ -1138,14 +1234,16 @@ const giftEntryButton = $("#giftEntryButton"); const banEntryButton = $("#banEntryButton"); const unbanEntryButton = $("#unbanEntryButton"); + const bdLeaderListButton = $("#bdLeaderListButton"); const refreshBDLeaderButton = $("#refreshBDLeaderButton"); + const addBDLeaderConfirmButton = $("#addBDLeaderConfirmButton"); const sendButton = $("#sendPropsButton"); const changeCountryButton = $("#changeCountryButton"); const banButton = $("#banButton"); const unbanButton = $("#unbanButton"); if (addBDLeaderButton) { const disabledByFeature = !featureEnabled("addBdLeader"); - addBDLeaderButton.disabled = disabledByFeature || !state.target || state.addingBDLeader || isTargetBDLeader(); + addBDLeaderButton.disabled = disabledByFeature || state.addingBDLeader; addBDLeaderButton.classList.toggle("is-feature-disabled", disabledByFeature); const title = addBDLeaderButton.querySelector(".action-item-title"); if (title) { @@ -1156,33 +1254,38 @@ } if (giftEntryButton) { const disabledByFeature = !featureEnabled("giftProps"); - giftEntryButton.disabled = disabledByFeature || !state.target; + giftEntryButton.disabled = disabledByFeature; giftEntryButton.classList.toggle("is-feature-disabled", disabledByFeature); const title = giftEntryButton.querySelector(".action-item-title"); if (title) title.textContent = disabledByFeature ? message("gift_props_disabled") : message("gift_props"); } if (countryEntryButton) { const disabledByFeature = !featureEnabled("changeCountry"); - countryEntryButton.disabled = disabledByFeature || !state.target || state.countriesLoading || state.changingCountry; + countryEntryButton.disabled = disabledByFeature || state.changingCountry; countryEntryButton.classList.toggle("is-feature-disabled", disabledByFeature); const title = countryEntryButton.querySelector(".action-item-title"); if (title) title.textContent = disabledByFeature ? message("change_country_disabled") : message("change_country"); } if (banEntryButton) { const disabledByFeature = !featureEnabled("banUser"); - banEntryButton.disabled = disabledByFeature || !state.target; + banEntryButton.disabled = disabledByFeature; banEntryButton.classList.toggle("is-feature-disabled", disabledByFeature); const title = banEntryButton.querySelector(".action-item-title"); if (title) title.textContent = disabledByFeature ? message("ban_user_disabled") : message("ban_user"); } if (unbanEntryButton) { const disabledByFeature = !featureEnabled("unbanUser"); - unbanEntryButton.disabled = disabledByFeature || !state.target; + unbanEntryButton.disabled = disabledByFeature; unbanEntryButton.classList.toggle("is-feature-disabled", disabledByFeature); const title = unbanEntryButton.querySelector(".action-item-title"); if (title) title.textContent = disabledByFeature ? message("unban_user_disabled") : message("unban_user"); } + if (bdLeaderListButton) bdLeaderListButton.disabled = state.bdLeadersLoading; if (refreshBDLeaderButton) refreshBDLeaderButton.disabled = state.bdLeadersLoading; + if (addBDLeaderConfirmButton) { + addBDLeaderConfirmButton.disabled = !featureEnabled("addBdLeader") || !state.target || state.addingBDLeader || isTargetBDLeader(); + addBDLeaderConfirmButton.textContent = state.addingBDLeader ? message("adding_bd_leader") : message("add_bd_leader"); + } if (sendButton) sendButton.disabled = !featureEnabled("giftProps") || !state.target || !state.selectedPropsId; if (changeCountryButton) { const currentID = String(state.target?.countryId || matchingCountryId(state.target) || ""); @@ -1252,26 +1355,29 @@ async function searchUser(event) { event.preventDefault(); - const input = $("#accountInput"); + const input = $("#modalAccountInput"); + const status = $("#modalSearchStatus"); + const button = $("#modalSearchButton"); const account = String(input?.value || "").trim(); if (!account) { - setStatus($("#searchStatus"), message("enter_user"), "error"); + setStatus(status, message("enter_user"), "error"); return; } - $("#searchButton").disabled = true; - setStatus($("#searchStatus"), message("searching")); + button.disabled = true; + setStatus(status, message("searching")); try { const body = await requestGoJSON(endpointSearchUser(account)); state.target = body?.user || body || null; renderTarget(); - setStatus($("#searchStatus"), ""); + await prepareActiveActionAfterTarget(); + setStatus(status, ""); } catch (error) { state.target = null; renderTarget(); - setStatus($("#searchStatus"), friendlyError(error), "error"); + setStatus(status, friendlyError(error), "error"); } finally { - $("#searchButton").disabled = false; + button.disabled = false; } } @@ -1280,13 +1386,18 @@ showToast(message("add_bd_leader_disabled")); return; } - if (!state.target || state.addingBDLeader) return; + if (!state.target) { + setStatus($("#addBDLeaderStatus"), message("enter_user"), "error"); + return; + } + if (state.addingBDLeader) return; if (isTargetBDLeader()) { - showToast(message("user_already_bd_leader")); + setStatus($("#addBDLeaderStatus"), message("user_already_bd_leader"), "error"); return; } state.addingBDLeader = true; + setStatus($("#addBDLeaderStatus"), message("adding_bd_leader")); updateActionState(); try { await requestGoJSON(endpointBDLeaders(), { @@ -1295,10 +1406,12 @@ userId: displayUserID(state.target) } }); + setStatus($("#addBDLeaderStatus"), ""); + closeModals(); showToast(message("add_bd_leader_success")); await loadBDLeaders(); } catch (error) { - showToast(friendlyError(error)); + setStatus($("#addBDLeaderStatus"), friendlyError(error), "error"); } finally { state.addingBDLeader = false; updateActionState(); @@ -1310,7 +1423,11 @@ showToast(message("gift_props_disabled")); return; } - if (!state.target || !state.selectedPropsId) return; + if (!state.target) { + setStatus($("#propsStatus"), message("enter_user"), "error"); + return; + } + if (!state.selectedPropsId) return; const button = $("#sendPropsButton"); button.disabled = true; @@ -1322,7 +1439,7 @@ propsId: state.selectedPropsId } }); - setModalOpen("#giftModal", false); + closeModals(); showToast(message("send_success")); } catch (error) { showToast(friendlyError(error)); @@ -1336,6 +1453,10 @@ setStatus($("#banStatus"), message("ban_user_disabled"), "error"); return; } + if (!state.target) { + setStatus($("#banStatus"), message("enter_user"), "error"); + return; + } if (!canBanTarget(state.target)) return; const button = $("#banButton"); @@ -1351,8 +1472,8 @@ }); state.target.accountStatus = "ARCHIVE"; renderTarget(); - setModalOpen("#banModal", false); setStatus($("#banStatus"), ""); + closeModals(); showToast(message("ban_success")); } catch (error) { setStatus($("#banStatus"), friendlyError(error), "error"); @@ -1366,6 +1487,10 @@ setStatus($("#unbanStatus"), message("unban_user_disabled"), "error"); return; } + if (!state.target) { + setStatus($("#unbanStatus"), message("enter_user"), "error"); + return; + } if (!canUnbanTarget(state.target)) return; const button = $("#unbanButton"); @@ -1381,8 +1506,8 @@ }); state.target.accountStatus = "NORMAL"; renderTarget(); - setModalOpen("#unbanModal", false); setStatus($("#unbanStatus"), ""); + closeModals(); showToast(message("unban_success")); } catch (error) { setStatus($("#unbanStatus"), friendlyError(error), "error"); @@ -1392,7 +1517,10 @@ } async function changeUserCountry() { - if (!state.target) return; + if (!state.target) { + setStatus($("#countryStatus"), message("enter_user"), "error"); + return; + } if (!featureEnabled("changeCountry")) { setStatus($("#countryStatus"), message("change_country_disabled"), "error"); return; @@ -1415,8 +1543,8 @@ }); state.target = body?.user || state.target; renderTarget(); - setModalOpen("#countryModal", false); setStatus($("#countryStatus"), ""); + closeModals(); showToast(message("country_changed")); } catch (error) { setStatus($("#countryStatus"), friendlyError(error), "error"); @@ -1467,7 +1595,8 @@ setPropsTypeMenuOpen(false); setCountryMenuOpen(false); }); - $("#addBDLeaderButton")?.addEventListener("click", addBDLeader); + $("#addBDLeaderButton")?.addEventListener("click", () => openActionModal("addBDLeader")); + $("#bdLeaderListButton")?.addEventListener("click", () => openActionModal("bdLeaders")); $("#refreshBDLeaderButton")?.addEventListener("click", () => { loadBDLeaders().catch((error) => { showToast(friendlyError(error)); @@ -1510,7 +1639,8 @@ setCountryMenuOpen(false); closeModals(); }); - $("#searchForm")?.addEventListener("submit", searchUser); + $("#modalSearchForm")?.addEventListener("submit", searchUser); + $("#addBDLeaderConfirmButton")?.addEventListener("click", addBDLeader); $("#sendPropsButton")?.addEventListener("click", sendProps); $("#banButton")?.addEventListener("click", banUser); $("#unbanButton")?.addEventListener("click", unbanUser); @@ -1523,7 +1653,7 @@ await applyLanguage(currentLang); if (!readRawParam("token")) { setLoading(false); - setStatus($("#searchStatus"), message("missing_token"), "error"); + setStatus($("#pageStatus"), message("missing_token"), "error"); return; } diff --git a/h5/hyapp/manager-center/style.css b/h5/hyapp/manager-center/style.css index 50d8f11..fee4e06 100644 --- a/h5/hyapp/manager-center/style.css +++ b/h5/hyapp/manager-center/style.css @@ -278,19 +278,22 @@ button:disabled { .action-card { display: grid; - gap: 4px; - padding: 6px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + padding: 12px; } .action-item { + position: relative; display: grid; - grid-template-columns: 42px minmax(0, 1fr) 24px; - gap: 10px; + grid-template-columns: 44px minmax(0, 1fr); + gap: 8px; align-items: center; width: 100%; - min-height: 58px; + min-height: 112px; + border: 1px solid var(--line); border-radius: 6px; - padding: 8px; + padding: 12px; background: #fff; color: var(--text); text-align: left; @@ -313,8 +316,8 @@ button:disabled { display: inline-flex; align-items: center; justify-content: center; - width: 42px; - height: 42px; + width: 44px; + height: 44px; border-radius: 8px; background: #e7f8f4; color: var(--primary-dark); @@ -338,6 +341,8 @@ button:disabled { .action-item-copy { display: grid; + grid-column: 1 / -1; + gap: 4px; min-width: 0; } @@ -349,8 +354,19 @@ button:disabled { white-space: nowrap; } +.action-item-subtitle { + overflow: hidden; + color: var(--muted); + font-size: 12px; + font-weight: 800; + text-overflow: ellipsis; + white-space: nowrap; +} + .action-item-arrow { - justify-self: end; + position: absolute; + top: 14px; + right: 12px; color: #9aa4af; } @@ -394,6 +410,10 @@ button:disabled { .bd-leader-list { display: grid; gap: 8px; + max-height: min(54vh, 420px); + overflow-y: auto; + padding-right: 2px; + overscroll-behavior: contain; } .bd-leader-row { @@ -814,6 +834,11 @@ textarea:focus { margin-bottom: 10px; } +.modal-target-card, +.operation-panel { + margin-top: 12px; +} + .modal-body textarea { display: block; }