From dcc1938e639219786c699217ec354a3217cfb86a Mon Sep 17 00:00:00 2001 From: zhx Date: Thu, 4 Jun 2026 19:29:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=AC=E4=BC=9A=E7=9B=B8=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/api.js | 14 + common/locales/ar.json | 31 +- common/locales/en.json | 31 +- common/locales/es.json | 31 +- common/locales/id.json | 31 +- common/locales/tr.json | 31 +- common/locales/zh.json | 31 +- gonghui/agency-center/index.html | 27 +- gonghui/agency-center/script.js | 51 +- gonghui/agency-center/style.css | 60 ++- gonghui/common/center.css | 3 +- gonghui/host-center/index.html | 61 ++- gonghui/host-center/script.js | 576 ++++++++++++++++++---- gonghui/host-center/style.css | 261 +++++++++- gonghui/superadmin-center/index.html | 362 ++++++++++++++ gonghui/superadmin-center/script.js | 696 +++++++++++++++++++++++++++ gonghui/superadmin-center/style.css | 535 ++++++++++++++++++++ 17 files changed, 2691 insertions(+), 141 deletions(-) create mode 100644 gonghui/superadmin-center/index.html create mode 100644 gonghui/superadmin-center/script.js create mode 100644 gonghui/superadmin-center/style.css diff --git a/common/api.js b/common/api.js index 2781d6c..79ecd3a 100644 --- a/common/api.js +++ b/common/api.js @@ -305,6 +305,19 @@ var default_api = 'https://api.global-interaction.com/'; }, }; + var hostCenterAPI = { + agency: function () { + return request('/api/v1/host-center/agency', { + method: 'GET', + }); + }, + platformPolicy: function () { + return request('/api/v1/host-center/platform-policy', { + method: 'GET', + }); + }, + }; + var levelAPI = { overview: function () { return request('/api/v1/levels/me/overview', { method: 'GET' }); @@ -352,6 +365,7 @@ var default_api = 'https://api.global-interaction.com/'; user: userAPI, wallet: walletAPI, host: hostAPI, + hostCenter: hostCenterAPI, agencyCenter: agencyCenterAPI, level: levelAPI, }; diff --git a/common/locales/ar.json b/common/locales/ar.json index b89225b..c84e01b 100644 --- a/common/locales/ar.json +++ b/common/locales/ar.json @@ -40,9 +40,10 @@ "host_center.minutes": "الدقائق", "host_center.gift_task": "مهمة الهدايا", "host_center.in_progress": "قيد التنفيذ", + "host_center.current_diamonds": "الألماس الحالي: {value}", "host_center.level_progress": "تقدم المستوى", "host_center.current_level": "المستوى الحالي", - "host_center.next_level_need": "المطلوب للمستوى التالي", + "host_center.next_level_need": "الألماس المطلوب للمستوى التالي", "host_center.level_value": "المستوى {level}", "host_center.platform_policy": "سياسة المنصة", "host_center.exchange_coins": "تبديل العملات", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "تم إرسال طلب السحب", "withdraw_exchange.exchange_success": "تم التبديل بنجاح", "withdraw_exchange.transfer_success": "تم التحويل بنجاح", + "superadmin_center.back": "رجوع", + "superadmin_center.title": "مركز المسؤول الأعلى", + "superadmin_center.change_language": "تغيير اللغة", + "superadmin_center.role": "مسؤول أعلى", + "superadmin_center.salary": "الراتب", + "superadmin_center.withdraw_exchange": "سحب/تبديل", + "superadmin_center.salary_history": "سجل الراتب", + "superadmin_center.invite_bd": "دعوة BD", + "superadmin_center.invite_agent": "دعوة وكيل", + "superadmin_center.bd_list": "قائمة BD", + "superadmin_center.agency_list": "قائمة الوكالات", + "superadmin_center.close": "إغلاق", + "superadmin_center.enter_user_id": "أدخل معرف المستخدم", + "superadmin_center.search": "بحث", + "superadmin_center.no_users_found": "لم يتم العثور على مستخدمين", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "عدد الوكالات", + "superadmin_center.bd_number": "عدد BD", + "superadmin_center.hosts": "المضيفون", + "superadmin_center.agencies": "الوكالات", + "superadmin_center.team_salary": "راتب الفريق", + "superadmin_center.team_recharge": "شحن الفريق", + "superadmin_center.total_income": "إجمالي الدخل", + "superadmin_center.previous_income": "دخل الفترة السابقة", + "superadmin_center.invite": "دعوة", + "superadmin_center.invite_success": "تم إرسال الدعوة.", "bd_center.back": "رجوع", "bd_center.title": "مركز BD", "bd_center.change_language": "تغيير اللغة", @@ -146,6 +173,8 @@ "agency_center.back": "رجوع", "agency_center.change_language": "تغيير اللغة", "agency_center.open_messages": "فتح الرسائل", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "وقت العمل (دقيقة)", "agency_center.work_time_short": "العمل", "agency_center.target": "الهدف", diff --git a/common/locales/en.json b/common/locales/en.json index 6866cd3..ce84c17 100644 --- a/common/locales/en.json +++ b/common/locales/en.json @@ -40,9 +40,10 @@ "host_center.minutes": "Minutes", "host_center.gift_task": "Gift Task", "host_center.in_progress": "In progress", + "host_center.current_diamonds": "Current diamonds: {value}", "host_center.level_progress": "Level progress", "host_center.current_level": "Current level", - "host_center.next_level_need": "Needed for next level", + "host_center.next_level_need": "Needed diamonds for next level", "host_center.level_value": "Lv. {level}", "host_center.platform_policy": "Platform policy", "host_center.exchange_coins": "Exchange coins", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "Withdrawal submitted", "withdraw_exchange.exchange_success": "Exchange success", "withdraw_exchange.transfer_success": "Transfer success", + "superadmin_center.back": "Back", + "superadmin_center.title": "Super Admin Center", + "superadmin_center.change_language": "Change language", + "superadmin_center.role": "Super Admin", + "superadmin_center.salary": "Salary", + "superadmin_center.withdraw_exchange": "Withdraw/Exchange", + "superadmin_center.salary_history": "Salary History", + "superadmin_center.invite_bd": "Invite BD", + "superadmin_center.invite_agent": "Invite Agent", + "superadmin_center.bd_list": "BD List", + "superadmin_center.agency_list": "Agency List", + "superadmin_center.close": "Close", + "superadmin_center.enter_user_id": "Enter User ID", + "superadmin_center.search": "Search", + "superadmin_center.no_users_found": "No users found", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "Agency number", + "superadmin_center.bd_number": "BD number", + "superadmin_center.hosts": "Hosts", + "superadmin_center.agencies": "Agencies", + "superadmin_center.team_salary": "Team salary", + "superadmin_center.team_recharge": "Team recharge", + "superadmin_center.total_income": "Total income", + "superadmin_center.previous_income": "Previous income", + "superadmin_center.invite": "Invite", + "superadmin_center.invite_success": "Invitation submitted.", "bd_center.back": "Back", "bd_center.title": "BD Center", "bd_center.change_language": "Change language", @@ -146,6 +173,8 @@ "agency_center.back": "Back", "agency_center.change_language": "Change language", "agency_center.open_messages": "Open messages", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "Work Time (min)", "agency_center.work_time_short": "Work", "agency_center.target": "Target", diff --git a/common/locales/es.json b/common/locales/es.json index 646a92e..3d05455 100644 --- a/common/locales/es.json +++ b/common/locales/es.json @@ -40,9 +40,10 @@ "host_center.minutes": "Minutos", "host_center.gift_task": "Tarea de regalos", "host_center.in_progress": "En progreso", + "host_center.current_diamonds": "Diamantes actuales: {value}", "host_center.level_progress": "Progreso de nivel", "host_center.current_level": "Nivel actual", - "host_center.next_level_need": "Necesario para el siguiente nivel", + "host_center.next_level_need": "Diamantes necesarios para el siguiente nivel", "host_center.level_value": "Nv. {level}", "host_center.platform_policy": "Política de plataforma", "host_center.exchange_coins": "Cambiar monedas", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "Retiro enviado", "withdraw_exchange.exchange_success": "Intercambio exitoso", "withdraw_exchange.transfer_success": "Transferencia exitosa", + "superadmin_center.back": "Volver", + "superadmin_center.title": "Centro Super Admin", + "superadmin_center.change_language": "Cambiar idioma", + "superadmin_center.role": "Super Admin", + "superadmin_center.salary": "Salario", + "superadmin_center.withdraw_exchange": "Retirar/Intercambiar", + "superadmin_center.salary_history": "Historial salarial", + "superadmin_center.invite_bd": "Invitar BD", + "superadmin_center.invite_agent": "Invitar agente", + "superadmin_center.bd_list": "Lista BD", + "superadmin_center.agency_list": "Lista de agencias", + "superadmin_center.close": "Cerrar", + "superadmin_center.enter_user_id": "Ingresa ID de usuario", + "superadmin_center.search": "Buscar", + "superadmin_center.no_users_found": "No se encontraron usuarios", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "Número de agencias", + "superadmin_center.bd_number": "Número de BD", + "superadmin_center.hosts": "Hosts", + "superadmin_center.agencies": "Agencias", + "superadmin_center.team_salary": "Salario del equipo", + "superadmin_center.team_recharge": "Recarga del equipo", + "superadmin_center.total_income": "Ingreso total", + "superadmin_center.previous_income": "Ingreso anterior", + "superadmin_center.invite": "Invitar", + "superadmin_center.invite_success": "Invitación enviada.", "bd_center.back": "Volver", "bd_center.title": "Centro BD", "bd_center.change_language": "Cambiar idioma", @@ -146,6 +173,8 @@ "agency_center.back": "Volver", "agency_center.change_language": "Cambiar idioma", "agency_center.open_messages": "Abrir mensajes", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "Tiempo de trabajo (min)", "agency_center.work_time_short": "Trabajo", "agency_center.target": "Objetivo", diff --git a/common/locales/id.json b/common/locales/id.json index aa662d3..8d17e6d 100644 --- a/common/locales/id.json +++ b/common/locales/id.json @@ -40,9 +40,10 @@ "host_center.minutes": "Menit", "host_center.gift_task": "Tugas hadiah", "host_center.in_progress": "Berlangsung", + "host_center.current_diamonds": "Diamond saat ini: {value}", "host_center.level_progress": "Progress level", "host_center.current_level": "Level saat ini", - "host_center.next_level_need": "Dibutuhkan ke level berikutnya", + "host_center.next_level_need": "Diamond yang dibutuhkan ke level berikutnya", "host_center.level_value": "Lv. {level}", "host_center.platform_policy": "Kebijakan platform", "host_center.exchange_coins": "Tukar coin", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "Pengajuan penarikan terkirim", "withdraw_exchange.exchange_success": "Penukaran berhasil", "withdraw_exchange.transfer_success": "Transfer berhasil", + "superadmin_center.back": "Kembali", + "superadmin_center.title": "Pusat Super Admin", + "superadmin_center.change_language": "Ganti bahasa", + "superadmin_center.role": "Super Admin", + "superadmin_center.salary": "Gaji", + "superadmin_center.withdraw_exchange": "Tarik/Tukar", + "superadmin_center.salary_history": "Riwayat Gaji", + "superadmin_center.invite_bd": "Undang BD", + "superadmin_center.invite_agent": "Undang agent", + "superadmin_center.bd_list": "Daftar BD", + "superadmin_center.agency_list": "Daftar agency", + "superadmin_center.close": "Tutup", + "superadmin_center.enter_user_id": "Masukkan ID pengguna", + "superadmin_center.search": "Cari", + "superadmin_center.no_users_found": "Pengguna tidak ditemukan", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "Jumlah agency", + "superadmin_center.bd_number": "Jumlah BD", + "superadmin_center.hosts": "Host", + "superadmin_center.agencies": "Agency", + "superadmin_center.team_salary": "Gaji tim", + "superadmin_center.team_recharge": "Recharge tim", + "superadmin_center.total_income": "Total pendapatan", + "superadmin_center.previous_income": "Pendapatan sebelumnya", + "superadmin_center.invite": "Undang", + "superadmin_center.invite_success": "Undangan terkirim.", "bd_center.back": "Kembali", "bd_center.title": "Pusat BD", "bd_center.change_language": "Ganti bahasa", @@ -146,6 +173,8 @@ "agency_center.back": "Kembali", "agency_center.change_language": "Ganti bahasa", "agency_center.open_messages": "Buka pesan", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "Waktu Kerja (mnt)", "agency_center.work_time_short": "Kerja", "agency_center.target": "Target", diff --git a/common/locales/tr.json b/common/locales/tr.json index 276f1b1..7b258bd 100644 --- a/common/locales/tr.json +++ b/common/locales/tr.json @@ -40,9 +40,10 @@ "host_center.minutes": "Dakika", "host_center.gift_task": "Hediye görevi", "host_center.in_progress": "Devam ediyor", + "host_center.current_diamonds": "Mevcut elmas: {value}", "host_center.level_progress": "Seviye ilerlemesi", "host_center.current_level": "Mevcut seviye", - "host_center.next_level_need": "Sonraki seviye için gereken", + "host_center.next_level_need": "Sonraki seviye için gereken elmas", "host_center.level_value": "Sv. {level}", "host_center.platform_policy": "Platform kuralları", "host_center.exchange_coins": "Coin değiştir", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "Çekim talebi gönderildi", "withdraw_exchange.exchange_success": "Değişim başarılı", "withdraw_exchange.transfer_success": "Transfer başarılı", + "superadmin_center.back": "Geri", + "superadmin_center.title": "Süper Admin Merkezi", + "superadmin_center.change_language": "Dili değiştir", + "superadmin_center.role": "Süper Admin", + "superadmin_center.salary": "Maaş", + "superadmin_center.withdraw_exchange": "Çekim/Değişim", + "superadmin_center.salary_history": "Maaş Geçmişi", + "superadmin_center.invite_bd": "BD Davet Et", + "superadmin_center.invite_agent": "Ajans Davet Et", + "superadmin_center.bd_list": "BD Listesi", + "superadmin_center.agency_list": "Ajans Listesi", + "superadmin_center.close": "Kapat", + "superadmin_center.enter_user_id": "Kullanıcı ID girin", + "superadmin_center.search": "Ara", + "superadmin_center.no_users_found": "Kullanıcı bulunamadı", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "Ajans sayısı", + "superadmin_center.bd_number": "BD sayısı", + "superadmin_center.hosts": "Hostlar", + "superadmin_center.agencies": "Ajanslar", + "superadmin_center.team_salary": "Takım maaşı", + "superadmin_center.team_recharge": "Takım yüklemesi", + "superadmin_center.total_income": "Toplam gelir", + "superadmin_center.previous_income": "Önceki gelir", + "superadmin_center.invite": "Davet et", + "superadmin_center.invite_success": "Davet gönderildi.", "bd_center.back": "Geri", "bd_center.title": "BD Merkezi", "bd_center.change_language": "Dili değiştir", @@ -146,6 +173,8 @@ "agency_center.back": "Geri", "agency_center.change_language": "Dili değiştir", "agency_center.open_messages": "Mesajları aç", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "Çalışma süresi (dk)", "agency_center.work_time_short": "Çalışma", "agency_center.target": "Hedef", diff --git a/common/locales/zh.json b/common/locales/zh.json index 832d004..4ce3e82 100644 --- a/common/locales/zh.json +++ b/common/locales/zh.json @@ -40,9 +40,10 @@ "host_center.minutes": "时长", "host_center.gift_task": "礼物任务", "host_center.in_progress": "进行中", + "host_center.current_diamonds": "当前钻石: {value}", "host_center.level_progress": "等级进度", "host_center.current_level": "当前等级", - "host_center.next_level_need": "距离下一级需要", + "host_center.next_level_need": "距离下一级所需钻石", "host_center.level_value": "Lv. {level}", "host_center.platform_policy": "平台政策", "host_center.exchange_coins": "兑换金币", @@ -114,6 +115,32 @@ "withdraw_exchange.withdraw_success": "提现申请已提交", "withdraw_exchange.exchange_success": "兑换成功", "withdraw_exchange.transfer_success": "转账成功", + "superadmin_center.back": "返回", + "superadmin_center.title": "超管中心", + "superadmin_center.change_language": "切换语言", + "superadmin_center.role": "超级管理员", + "superadmin_center.salary": "薪资", + "superadmin_center.withdraw_exchange": "提现/兑换", + "superadmin_center.salary_history": "薪资历史", + "superadmin_center.invite_bd": "邀请 BD", + "superadmin_center.invite_agent": "邀请代理", + "superadmin_center.bd_list": "BD 列表", + "superadmin_center.agency_list": "公会列表", + "superadmin_center.close": "关闭", + "superadmin_center.enter_user_id": "输入用户 ID", + "superadmin_center.search": "搜索", + "superadmin_center.no_users_found": "未找到用户", + "superadmin_center.uid_prefix": "UID: {id}", + "superadmin_center.agency_number": "公会数量", + "superadmin_center.bd_number": "BD 数量", + "superadmin_center.hosts": "主播", + "superadmin_center.agencies": "公会", + "superadmin_center.team_salary": "团队薪资", + "superadmin_center.team_recharge": "团队充值", + "superadmin_center.total_income": "总收入", + "superadmin_center.previous_income": "上期收入", + "superadmin_center.invite": "邀请", + "superadmin_center.invite_success": "邀请已提交。", "bd_center.back": "返回", "bd_center.title": "BD Center", "bd_center.change_language": "切换语言", @@ -146,6 +173,8 @@ "agency_center.back": "返回", "agency_center.change_language": "切换语言", "agency_center.open_messages": "打开消息", + "agency_center.agency_short_id": "ID: {id}", + "agency_center.bd_label": "BD:", "agency_center.work_time": "Work Time (min)", "agency_center.work_time_short": "Work", "agency_center.target": "Target", diff --git a/gonghui/agency-center/index.html b/gonghui/agency-center/index.html index b3ff151..4bd7ad1 100644 --- a/gonghui/agency-center/index.html +++ b/gonghui/agency-center/index.html @@ -65,8 +65,31 @@
Yumi Star Agency
-
UID: 163003
-
BD: Mila
+
ID: 163003
+
+ + + - + - - + + diff --git a/gonghui/host-center/script.js b/gonghui/host-center/script.js index a708eca..3bdc958 100644 --- a/gonghui/host-center/script.js +++ b/gonghui/host-center/script.js @@ -1,47 +1,64 @@ (function () { var HOST_SALARY_USD = 'HOST_SALARY_USD'; - var HOST_LEVEL_TRACK = 'charm'; - var mock = { - profile: { - name: 'quick_1780052629537', - uid: '163000', - avatar: avatarData('Y', '#dbc8ff', '#7d57c7'), - }, - agency: { - shortId: '163003', - }, - salary: { - available: 30650.75, - monthKey: 'host_center.month_june', - month: 'June', - currencyKey: 'host_center.currency_usd', - currency: 'USD', - useCurrentMonth: false, - }, - level: { - current: 3, - currentRequired: 0, - currentValue: 2450, - nextValue: 4000, - }, - }; - - var state = cloneHostState(mock); + var state = emptyHostState(); + var policyAutoScrollPending = false; function $(id) { return document.getElementById(id); } - function cloneHostState(value) { + function emptyHostState() { return { - profile: Object.assign({}, value.profile), - agency: Object.assign({}, value.agency), - salary: Object.assign({}, value.salary), - level: Object.assign({}, value.level), + profile: { + loading: true, + name: '', + uid: '', + avatar: '', + }, + agency: { + loading: true, + shortId: '', + name: '', + avatar: '', + }, + salary: { + loading: true, + available: 0, + monthKey: '', + month: '', + currencyKey: '', + currency: '', + useCurrentMonth: false, + }, + level: { + loading: true, + current: 0, + nextLevelNo: 0, + currentRequired: 0, + currentValue: 0, + nextValue: 0, + needed: 0, + }, + policy: { + loading: true, + loaded: false, + found: false, + policy: null, + }, }; } + function setSkeleton(element, loading, text) { + if (!element) return; + element.classList.toggle('skeleton-line', !!loading); + if (loading) { + element.textContent = ''; + return; + } + element.textContent = text || ''; + } + function avatarData(text, background, foreground) { var value = String(text || 'Y') @@ -76,6 +93,13 @@ }); } + function integer(value) { + var number = Number(value || 0); + return number.toLocaleString('en-US', { + maximumFractionDigits: 0, + }); + } + function initial(text) { return String(text || '') .trim() @@ -83,14 +107,6 @@ .toUpperCase(); } - function queryValue(name) { - return new URLSearchParams(window.location.search).get(name); - } - - function isMockMode() { - return queryValue('mock') === '1'; - } - function hasAccessToken() { return ( window.HyAppAPI && @@ -168,26 +184,73 @@ return salary.currency || t('host_center.currency_usd', 'USD'); } + function formatMode(value) { + var text = String(value || '') + .replace(/[_-]+/g, ' ') + .trim(); + if (!text) return '-'; + return text.replace(/\b\w/g, function (letter) { + return letter.toUpperCase(); + }); + } + + function formatDate(ms) { + var value = toNumber(ms); + if (!value) return ''; + try { + return new Date(value).toLocaleDateString(currentLocale(), { + year: 'numeric', + month: 'short', + day: 'numeric', + }); + } catch (_) { + return ''; + } + } + + function policyAmount(minor) { + return '$' + money(toNumber(minor) / 100); + } + function renderProfile() { var image = $('profileAvatar'); var fallback = $('profileAvatarFallback'); var badge = $('profileBadge'); var profile = state.profile; var agency = state.agency; + var loading = !!profile.loading; + + setSkeleton($('profileName'), loading, profile.name || ''); + setSkeleton( + $('profileUID'), + loading, + profile.uid ? 'UID: ' + profile.uid : '' + ); + fallback.classList.toggle('avatar-skeleton', loading); + if (loading) { + image.hidden = true; + image.removeAttribute('src'); + fallback.hidden = false; + fallback.textContent = ''; + badge.hidden = true; + badge.replaceChildren(); + badge.classList.remove('skeleton-line'); + return; + } - $('profileName').textContent = profile.name || 'User'; - $('profileUID').textContent = 'UID: ' + (profile.uid || '-'); fallback.textContent = initial(profile.name || profile.uid) || 'Y'; - if (agency.shortId) { + if (agency.loading) { badge.hidden = false; - badge.textContent = t( - 'host_center.agency_short_id', - 'Agency ID: {id}' - ).replace('{id}', agency.shortId); + setSkeleton(badge, true, ''); + } else if (agency.shortId) { + badge.hidden = false; + badge.classList.remove('skeleton-line'); + renderAgencyBadge(badge, agency); } else { badge.hidden = true; - badge.textContent = ''; + badge.classList.remove('skeleton-line'); + badge.replaceChildren(); } if (!profile.avatar) { @@ -207,23 +270,68 @@ image.src = profile.avatar; } + function renderAgencyBadge(container, agency) { + container.replaceChildren(); + var avatar = document.createElement('span'); + avatar.className = 'agency-badge-avatar'; + var fallbackText = + initial(agency.name || agency.shortId) || + t('host_center.agency', 'Agency').charAt(0); + if (agency.avatar) { + var img = document.createElement('img'); + img.src = agency.avatar; + img.alt = ''; + img.addEventListener('error', function () { + avatar.textContent = fallbackText; + }); + avatar.appendChild(img); + } else { + avatar.textContent = fallbackText; + } + + var text = document.createElement('span'); + text.className = 'agency-badge-id'; + text.textContent = t( + 'host_center.agency_short_id', + 'Agency ID: {id}' + ).replace('{id}', agency.shortId); + container.appendChild(avatar); + container.appendChild(text); + } + function renderIncome() { var salary = state.salary; - $('availableSalary').textContent = money(salary.available); - $('incomeFoot').textContent = t( - 'host_center.salary_month_note', - '{month} settlement · {currency}' - ) - .replace('{month}', salaryMonthLabel(salary)) - .replace('{currency}', salaryCurrencyLabel(salary)); + var loading = !!salary.loading; + setSkeleton($('availableSalary'), loading, money(salary.available)); + setSkeleton( + $('incomeFoot'), + loading, + t('host_center.salary_month_note', '{month} settlement · {currency}') + .replace('{month}', salaryMonthLabel(salary)) + .replace('{currency}', salaryCurrencyLabel(salary)) + ); + var coinIcon = document.querySelector('.coin-icon'); + if (coinIcon) coinIcon.classList.toggle('skeleton-coin', loading); } function renderLevel() { var level = state.level; + var loading = !!level.loading; + if (loading) { + setSkeleton($('currentLevel'), true, ''); + setSkeleton($('nextLevelNeed'), true, ''); + setSkeleton($('levelStatus'), true, ''); + $('levelProgress').style.width = '38%'; + $('levelProgress').classList.add('skeleton-fill'); + return; + } var currentRequired = toNumber(level.currentRequired); var currentValue = toNumber(level.currentValue); var nextValue = toNumber(level.nextValue); - var need = Math.max(0, nextValue - currentValue); + var need = + level.needed !== undefined && level.needed !== null + ? toNumber(level.needed) + : Math.max(0, nextValue - currentValue); var denominator = Math.max(1, nextValue - currentRequired); var currentInLevel = Math.max(0, currentValue - currentRequired); var percent = @@ -239,17 +347,169 @@ 'Lv. {level}' ).replace('{level}', String(level.current || 0)); $('nextLevelNeed').textContent = money(need); + $('currentLevel').classList.remove('skeleton-line'); + $('nextLevelNeed').classList.remove('skeleton-line'); + $('levelStatus').classList.remove('skeleton-line'); $('levelProgress').style.width = percent + '%'; + $('levelProgress').classList.remove('skeleton-fill'); $('levelStatus').textContent = t( - 'host_center.in_progress', - 'In progress' - ); + 'host_center.current_diamonds', + 'Current diamonds: {value}' + ).replace('{value}', integer(currentValue)); + } + + function appendPolicyCell(parent, label, value) { + var cell = document.createElement('div'); + cell.className = 'policy-cell'; + var labelNode = document.createElement('span'); + labelNode.className = 'policy-cell-label'; + labelNode.textContent = label; + var valueNode = document.createElement('strong'); + valueNode.className = 'policy-cell-value'; + valueNode.textContent = value; + cell.appendChild(labelNode); + cell.appendChild(valueNode); + parent.appendChild(cell); + } + + function policyHighlightLevelNo() { + var current = toNumber(state.level && state.level.current); + return current > 0 ? current : 0; + } + + function isPolicyOpen() { + var modal = $('policyModal'); + return !!modal && !modal.hidden; + } + + function scrollPolicyLevelIntoView(row) { + if (!policyAutoScrollPending || !row || !isPolicyOpen()) return; + policyAutoScrollPending = false; + var run = window.requestAnimationFrame || window.setTimeout; + run(function () { + try { + row.scrollIntoView({ + behavior: 'smooth', + block: 'center', + inline: 'nearest', + }); + } catch (_) { + var container = row.closest('.policy-sheet-body'); + if (container) { + container.scrollTop = + row.offsetTop - + Math.max(0, (container.clientHeight - row.offsetHeight) / 2); + } + } + }); + } + + function renderPolicy() { + var summary = state.policy || {}; + var policy = summary.policy || {}; + var list = $('policyLevelList'); + var empty = $('policyEmpty'); + if (!list || !empty) return; + + list.textContent = ''; + if (summary.loading) { + setSkeleton($('policyName'), true, ''); + setSkeleton($('policyMeta'), true, ''); + empty.hidden = false; + empty.textContent = ''; + empty.classList.add('policy-skeleton-block'); + return; + } + $('policyName').classList.remove('skeleton-line'); + $('policyMeta').classList.remove('skeleton-line'); + empty.classList.remove('policy-skeleton-block'); + if (!summary.found || !policy || !policy.name) { + $('policyName').textContent = 'Platform policy'; + $('policyMeta').textContent = ''; + empty.hidden = false; + empty.textContent = 'No platform policy configured.'; + policyAutoScrollPending = false; + return; + } + + var from = formatDate(policy.effective_from_ms); + var to = formatDate(policy.effective_to_ms); + var meta = [ + 'Mode: ' + formatMode(policy.settlement_mode), + 'Trigger: ' + formatMode(policy.settlement_trigger_mode), + ]; + if (from || to) { + meta.push('Effective: ' + (from || '-') + ' - ' + (to || 'No end')); + } + $('policyName').textContent = policy.name || 'Current policy'; + $('policyMeta').textContent = meta.join(' / '); + empty.hidden = true; + empty.textContent = ''; + + var levels = Array.isArray(policy.levels) ? policy.levels : []; + if (!levels.length) { + empty.hidden = false; + empty.textContent = 'No policy levels configured.'; + policyAutoScrollPending = false; + return; + } + + var highlightedRow = null; + var activeLevelNo = policyHighlightLevelNo(); + levels.forEach(function (level) { + var row = document.createElement('div'); + row.className = 'policy-level-row'; + var levelNo = toNumber(level.level_no); + row.dataset.levelNo = String(levelNo || ''); + if (levelNo > 0 && levelNo === activeLevelNo) { + row.className += ' is-current-level'; + highlightedRow = row; + } + var head = document.createElement('div'); + head.className = 'policy-row-head'; + var title = document.createElement('strong'); + title.className = 'policy-level'; + title.textContent = 'Lv. ' + (levelNo || '-'); + var required = document.createElement('span'); + required.className = 'policy-effective'; + required.textContent = + integer(level.required_diamonds) + ' diamonds'; + head.appendChild(title); + head.appendChild(required); + + var grid = document.createElement('div'); + grid.className = 'policy-grid'; + appendPolicyCell( + grid, + 'Host salary', + policyAmount(level.host_salary_usd_minor) + ); + appendPolicyCell( + grid, + 'Host coin reward', + integer(level.host_coin_reward) + ); + appendPolicyCell( + grid, + 'Agency salary', + policyAmount(level.agency_salary_usd_minor) + ); + row.appendChild(head); + row.appendChild(grid); + list.appendChild(row); + }); + if (highlightedRow) { + scrollPolicyLevelIntoView(highlightedRow); + } else { + policyAutoScrollPending = false; + } } function render() { renderProfile(); renderIncome(); renderLevel(); + renderPolicy(); } function applyProfile(payload) { @@ -271,6 +531,7 @@ var avatar = firstValue(profile, ['avatar', 'avatar_url', 'avatarUrl']); state.profile = { + loading: false, name: name || state.profile.name, uid: uid || state.profile.uid, avatar: @@ -283,18 +544,25 @@ }; } - function applyRoleSummary(payload) { - var summary = unwrapData(payload); - if (!summary) return; - - var agencyId = firstValue(summary, [ + function applyAgency(payload) { + var data = unwrapData(payload); + var agency = data && data.agency ? data.agency : data; + if (!agency) { + return; + } + var shortId = firstValue(agency, [ + 'short_id', + 'shortId', + 'display_user_id', + 'displayUserId', 'agency_id', 'agencyId', - 'host_agency_id', - 'hostAgencyId', ]); state.agency = { - shortId: agencyId ? String(agencyId) : '', + loading: false, + shortId: shortId ? String(shortId) : '', + name: firstValue(agency, ['name', 'agency_name', 'agencyName']), + avatar: firstValue(agency, ['avatar', 'avatar_url', 'avatarUrl']), }; } @@ -320,13 +588,25 @@ function applyWallet(payload) { var balance = findBalance(payload, HOST_SALARY_USD); - if (!balance) return; + if (!balance) { + state.salary = Object.assign({}, state.salary, { + loading: false, + available: 0, + monthKey: '', + month: '', + currencyKey: 'host_center.currency_usd', + currency: 'USD', + useCurrentMonth: true, + }); + return; + } var amount = firstValue(balance, [ 'available_amount', 'availableAmount', ]); state.salary = Object.assign({}, state.salary, { + loading: false, available: displayAmountForAsset(HOST_SALARY_USD, amount), monthKey: '', month: '', @@ -336,42 +616,61 @@ }); } - function pickLevelTrack(payload) { - var data = unwrapData(payload); - var tracks = data && Array.isArray(data.tracks) ? data.tracks : []; - if (!tracks.length) return null; - - for (var i = 0; i < tracks.length; i += 1) { - if (tracks[i].track === HOST_LEVEL_TRACK) return tracks[i]; - } - return tracks[0]; - } - - function applyLevel(payload) { - var track = pickLevelTrack(payload); - if (!track) return; - + function applyLevelProgress(progress) { + if (!progress) return; state.level = { + loading: false, current: toNumber( - firstValue(track, ['level', 'current_level', 'currentLevel']) + firstValue(progress, ['current_level', 'currentLevel']) + ), + nextLevelNo: toNumber( + firstValue(progress, ['next_level', 'nextLevel']) ), currentRequired: toNumber( - firstValue(track, [ - 'current_level_required_value', - 'currentLevelRequiredValue', + firstValue(progress, [ + 'current_required_diamonds', + 'currentRequiredDiamonds', ]) ), currentValue: toNumber( - firstValue(track, ['total_value', 'totalValue']) + firstValue(progress, ['current_value', 'currentValue']) ), nextValue: toNumber( - firstValue(track, [ - 'next_level_required_value', - 'nextLevelRequiredValue', - 'total_value', - 'totalValue', + firstValue(progress, [ + 'next_required_diamonds', + 'nextRequiredDiamonds', + 'current_value', + 'currentValue', ]) ), + needed: toNumber( + firstValue(progress, [ + 'needed_for_next_level', + 'neededForNextLevel', + ]) + ), + }; + } + + function applyPolicy(payload) { + var data = unwrapData(payload); + if (!data || !data.found || !data.policy) { + state.policy = { + loading: false, + loaded: true, + found: false, + policy: null, + }; + return; + } + applyLevelProgress( + firstValue(data, ['level_progress', 'levelProgress']) + ); + state.policy = { + loading: false, + loaded: true, + found: true, + policy: data.policy, }; } @@ -382,8 +681,52 @@ } } + function loadPolicy() { + if (!window.HyAppAPI || !window.HyAppAPI.hostCenter) { + renderPolicy(); + return Promise.resolve(false); + } + if (!hasAccessToken()) { + renderPolicy(); + return Promise.resolve(false); + } + state.policy = Object.assign({}, state.policy, { loading: true }); + renderPolicy(); + return safeRequest(function () { + return window.HyAppAPI.hostCenter.platformPolicy(); + }).then(function (payload) { + if (payload) { + applyPolicy(payload); + } else { + state.policy = { + loading: true, + loaded: false, + found: false, + policy: null, + }; + toast( + t( + 'host_center.load_failed', + 'Load failed. Try again later.' + ) + ); + } + renderPolicy(); + return !!payload; + }); + } + + function requestPolicy() { + if (!window.HyAppAPI || !window.HyAppAPI.hostCenter) { + return Promise.resolve(null); + } + return safeRequest(function () { + return window.HyAppAPI.hostCenter.platformPolicy(); + }); + } + function loadRealData() { - if (isMockMode() || !window.HyAppAPI) return Promise.resolve(false); + if (!window.HyAppAPI) return Promise.resolve(false); if (window.HyAppParams && window.HyAppParams.parse) { window.HyAppParams.parse(); } @@ -394,19 +737,19 @@ return window.HyAppAPI.user.me(); }), safeRequest(function () { - return window.HyAppAPI.user.roleSummary(); + return window.HyAppAPI.hostCenter.agency(); }), safeRequest(function () { return window.HyAppAPI.wallet.balances([HOST_SALARY_USD]); }), - safeRequest(function () { - return window.HyAppAPI.level.overview(); - }), + requestPolicy(), ]).then(function (results) { applyProfile(results[0]); - applyRoleSummary(results[1]); + applyAgency(results[1]); applyWallet(results[2]); - applyLevel(results[3]); + if (results[3]) { + applyPolicy(results[3]); + } render(); var hasAnyData = results.some(function (item) { @@ -431,6 +774,27 @@ '../withdraw-exchange/index.html?' + params.toString(); } + function openPolicy() { + var modal = $('policyModal'); + if (!modal) return; + policyAutoScrollPending = true; + modal.hidden = false; + modal.setAttribute('aria-hidden', 'false'); + document.body.classList.add('modal-open'); + renderPolicy(); + policyAutoScrollPending = true; + loadPolicy(); + } + + function closePolicy() { + var modal = $('policyModal'); + if (!modal) return; + modal.hidden = true; + modal.setAttribute('aria-hidden', 'true'); + document.body.classList.remove('modal-open'); + policyAutoScrollPending = false; + } + function bindEvents() { $('backButton').addEventListener('click', function () { if (window.HyAppBridge) window.HyAppBridge.back(); @@ -440,6 +804,16 @@ 'click', openWithdrawExchange ); + + document.querySelectorAll('[data-action="policy"]').forEach(function (button) { + button.addEventListener('click', openPolicy); + }); + document.querySelectorAll('[data-close-policy]').forEach(function (button) { + button.addEventListener('click', closePolicy); + }); + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape') closePolicy(); + }); } var initialized = false; @@ -453,7 +827,7 @@ if (window.HyAppBridge) { window.HyAppBridge.ready({ page: 'host-center', - mock: !loaded, + mock: false, }); } }); diff --git a/gonghui/host-center/style.css b/gonghui/host-center/style.css index db317e7..5866643 100644 --- a/gonghui/host-center/style.css +++ b/gonghui/host-center/style.css @@ -1,6 +1,8 @@ .profile-badge { display: inline-flex; align-items: center; + gap: 6px; + max-width: 100%; margin-top: 8px; padding: 5px 9px; border-radius: 999px; @@ -11,6 +13,35 @@ line-height: 1.2; } +.agency-badge-avatar { + display: inline-flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + flex: 0 0 22px; + overflow: hidden; + border-radius: 50%; + background: #dbc8ff; + color: var(--primary-deep); + font-size: 12px; + font-weight: 950; + line-height: 1; +} + +.agency-badge-avatar img { + width: 100%; + height: 100%; + object-fit: cover; +} + +.agency-badge-id { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + .income-card, .task-card { padding: 16px; @@ -77,6 +108,7 @@ display: inline-flex; align-items: center; justify-content: center; + max-width: 52%; min-height: 28px; padding: 0 10px; border-radius: 999px; @@ -84,6 +116,10 @@ color: var(--primary-deep); font-size: 12px; font-weight: 900; + overflow: hidden; + line-height: 1; + text-overflow: ellipsis; + white-space: nowrap; } .task-grid { @@ -99,13 +135,11 @@ .task-item > span { display: block; - overflow: hidden; + overflow-wrap: anywhere; color: var(--muted); font-size: 12px; font-weight: 800; line-height: 1.2; - text-overflow: ellipsis; - white-space: nowrap; } .task-item strong { @@ -134,6 +168,223 @@ transition: width 0.2s ease; } +.skeleton-line, +.avatar-skeleton, +.skeleton-coin, +.skeleton-fill, +.policy-skeleton-block { + position: relative; + display: inline-block; + overflow: hidden; + color: transparent !important; + background: linear-gradient( + 90deg, + #f2eafb 0%, + #eadcff 38%, + #f8f3ff 62%, + #f2eafb 100% + ) !important; + background-size: 220% 100% !important; + box-shadow: none !important; + animation: hostSkeleton 1.15s ease-in-out infinite; +} + +.name.skeleton-line { + width: min(260px, 78%); + height: 30px; + border-radius: 8px; +} + +.meta.skeleton-line { + width: 135px; + height: 18px; + margin-top: 9px; + border-radius: 7px; +} + +.profile-badge.skeleton-line { + width: 190px; + height: 28px; + border-radius: 999px; +} + +.avatar-skeleton { + width: 100%; + height: 100%; + border-radius: 50%; +} + +.income-value .skeleton-line { + width: 160px; + height: 38px; + border-radius: 10px; +} + +.income-foot.skeleton-line { + width: 190px; + height: 18px; + border-radius: 7px; +} + +.task-pill.skeleton-line { + width: 108px; + min-height: 28px; +} + +.task-item strong.skeleton-line { + width: 126px; + height: 23px; + border-radius: 8px; +} + +.skeleton-fill { + width: 38% !important; +} + +.policy-summary .skeleton-line { + border-radius: 8px; +} + +.policy-summary strong.skeleton-line { + width: 220px; + height: 22px; +} + +.policy-summary span.skeleton-line { + width: 260px; + max-width: 100%; + height: 16px; +} + +.policy-skeleton-block { + height: 132px; + margin-top: 14px; + border-radius: 8px; +} + +@keyframes hostSkeleton { + 0% { + background-position: 120% 0; + } + + 100% { + background-position: -120% 0; + } +} + +.policy-sheet-body { + min-height: 180px; + overflow-y: auto; + padding: 16px; +} + +.policy-summary { + display: flex; + flex-direction: column; + gap: 6px; + padding: 14px; + border-radius: 8px; + background: var(--hy-theme-primary-soft, #f4ebff); +} + +.policy-summary strong { + overflow-wrap: anywhere; + color: var(--text); + font-size: 17px; + font-weight: 950; + line-height: 1.25; +} + +.policy-summary span { + color: var(--muted); + font-size: 12px; + font-weight: 800; + line-height: 1.45; +} + +.policy-empty { + padding: 22px 10px; + color: var(--muted); + text-align: center; + font-size: 14px; + font-weight: 850; + line-height: 1.45; +} + +.policy-level-list { + display: flex; + flex-direction: column; + gap: 10px; + margin-top: 12px; +} + +.policy-level-row { + padding: 14px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fff; + scroll-margin: 24px; +} + +.policy-level-row.is-current-level { + border-color: var(--button, #7d57c7); + background: linear-gradient(180deg, #fbf8ff 0%, #fff 100%); + box-shadow: + 0 0 0 1px var(--button, #7d57c7), + 0 10px 22px rgba(125, 87, 199, 0.14); +} + +.policy-row-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 10px; +} + +.policy-level { + color: var(--text); + font-size: 16px; + font-weight: 950; + line-height: 1.25; +} + +.policy-effective { + color: var(--muted); + text-align: right; + font-size: 12px; + font-weight: 800; + line-height: 1.25; +} + +.policy-grid { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 10px; + margin-top: 12px; +} + +.policy-cell { + min-width: 0; +} + +.policy-cell-label { + display: block; + color: var(--muted); + font-size: 11px; + font-weight: 800; + line-height: 1.2; +} + +.policy-cell-value { + display: block; + margin-top: 4px; + overflow-wrap: anywhere; + color: #292d33; + font-size: 14px; + font-weight: 950; + line-height: 1.25; +} + @media (max-width: 360px) { .income-card, .task-card { @@ -148,4 +399,8 @@ .task-grid { grid-template-columns: 1fr; } + + .policy-grid { + grid-template-columns: 1fr; + } } diff --git a/gonghui/superadmin-center/index.html b/gonghui/superadmin-center/index.html new file mode 100644 index 0000000..0c6c6ac --- /dev/null +++ b/gonghui/superadmin-center/index.html @@ -0,0 +1,362 @@ + + + + + + Superadmin Center + + + + + + +
+ + + + +
+
+
+ + +
+
+
+ Super Admin +
+
-
+
UID: -
+
+
+ +
+
+

Salary

+ +
+
+ + 0.00 +
+
+ + + +
+
+ + +
+
+
+
+
+ + + + + + + + +
+ + + + + + + diff --git a/gonghui/superadmin-center/script.js b/gonghui/superadmin-center/script.js new file mode 100644 index 0000000..2e3aac7 --- /dev/null +++ b/gonghui/superadmin-center/script.js @@ -0,0 +1,696 @@ +(function () { + var state = { + activeTab: 'bd', + inviteMode: 'bd', + inviteQuery: '', + inviteStatus: '', + inviteStatusType: '', + selectedDetails: null, + }; + + var mock = { + profile: { + name: 'Admin Sophia', + uid: 'SA-100001', + avatar: avatarData('S', '#dbc8ff', '#7d57c7'), + }, + balance: { + available: 168250.5, + }, + overview: { + totalBD: 8, + totalAgency: 36, + totalHosts: 428, + teamSalary: 586420.8, + teamRecharge: 1268800, + }, + bdList: [ + { + id: '880001', + name: 'Mila BD', + avatar: avatarData('M', '#dbc8ff', '#7d57c7'), + agencies: 12, + hosts: 126, + salary: 128450.35, + recharge: 356900, + }, + { + id: '880018', + name: 'Jasmine BD', + avatar: avatarData('J', '#f4ebff', '#7d57c7'), + agencies: 9, + hosts: 104, + salary: 106280.4, + recharge: 298520, + }, + { + id: '880026', + name: 'Omar BD', + avatar: avatarData('O', '#efe4ff', '#7d57c7'), + agencies: 7, + hosts: 82, + salary: 86140, + recharge: 214330, + }, + ], + agencyList: [ + { + id: '163003', + name: 'Yumi Star Agency', + avatar: avatarData('Y', '#dbc8ff', '#7d57c7'), + bdId: '880001', + hosts: 28, + salary: 38420.55, + recharge: 126880, + }, + { + id: '163086', + name: 'Moon Live Agency', + avatar: avatarData('M', '#f4ebff', '#7d57c7'), + bdId: '880018', + hosts: 19, + salary: 29106.2, + recharge: 98540, + }, + { + id: '163221', + name: 'Nova Host Guild', + avatar: avatarData('N', '#efe4ff', '#7d57c7'), + bdId: '880026', + hosts: 16, + salary: 22450, + recharge: 73150, + }, + ], + history: [ + { + month: '2026-06', + status: 'In progress', + bdCount: 8, + agencyCount: 36, + totalSalary: 586420.8, + totalRecharge: 1268800, + }, + { + month: '2026-05', + status: 'Completed', + bdCount: 7, + agencyCount: 31, + totalSalary: 520180.25, + totalRecharge: 1112900, + }, + { + month: '2026-04', + status: 'Completed', + bdCount: 6, + agencyCount: 26, + totalSalary: 462300, + totalRecharge: 980400, + }, + ], + inviteRecords: { + bd: [ + { + id: 'bd-inv-01', + status: 'Pending', + profile: { + id: '3200901', + name: 'BD_River', + account: '880099', + avatar: avatarData('R', '#dbc8ff', '#7d57c7'), + }, + }, + ], + agent: [ + { + id: 'agent-inv-01', + status: 'Success', + profile: { + id: '3200801', + name: 'Agent_Lina', + account: '168801', + avatar: avatarData('L', '#f4ebff', '#7d57c7'), + }, + }, + ], + }, + searchPool: { + bd: [ + { + id: '3200902', + name: 'BD_Kai', + account: '880102', + avatar: avatarData('K', '#dbc8ff', '#7d57c7'), + }, + { + id: '3200903', + name: 'BD_Ayla', + account: '880103', + avatar: avatarData('A', '#efe4ff', '#7d57c7'), + }, + ], + agent: [ + { + id: '3200802', + name: 'Agent_Omar', + account: '168802', + avatar: avatarData('O', '#dbc8ff', '#7d57c7'), + }, + { + id: '3200803', + name: 'Agent_Sara', + account: '168803', + avatar: avatarData('S', '#efe4ff', '#7d57c7'), + }, + ], + }, + }; + + function $(id) { + return document.getElementById(id); + } + + function avatarData(text, background, foreground) { + var value = + String(text || 'S') + .trim() + .charAt(0) || 'S'; + var letter = value.replace(/[<>&"']/g, ''); + var svg = + '' + + '' + + '' + + letter + + '' + + ''; + return 'data:image/svg+xml;charset=UTF-8,' + encodeURIComponent(svg); + } + + function t(key, fallback) { + return window.HyAppI18n && window.HyAppI18n.t + ? window.HyAppI18n.t(key, fallback) + : fallback || key; + } + + function money(value) { + return Number(value || 0).toLocaleString('en-US', { + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }); + } + + function compactMoney(value) { + var amount = Number(value || 0); + var abs = Math.abs(amount); + if (abs >= 1000000) { + return ( + (amount / 1000000).toLocaleString('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + }) + 'M' + ); + } + if (abs >= 1000) { + return ( + (amount / 1000).toLocaleString('en-US', { + minimumFractionDigits: 0, + maximumFractionDigits: 2, + }) + 'K' + ); + } + return money(amount); + } + + function count(value) { + return Number(value || 0).toLocaleString('en-US', { + maximumFractionDigits: 0, + }); + } + + function initial(text) { + return String(text || '') + .trim() + .charAt(0) + .toUpperCase(); + } + + function toast(message) { + if (!message) return; + if (window.HyAppToast && window.HyAppToast.show) { + window.HyAppToast.show(message); + return; + } + window.alert(message); + } + + function setImage(image, fallback, src, name) { + if (!image || !fallback) return; + fallback.textContent = initial(name) || 'S'; + if (!src) { + image.hidden = true; + fallback.hidden = false; + return; + } + image.onload = function () { + image.hidden = false; + fallback.hidden = true; + }; + image.onerror = function () { + image.hidden = true; + fallback.hidden = false; + }; + image.src = src; + } + + function makeAvatar(item) { + var avatar = document.createElement('div'); + var fallback = document.createElement('span'); + avatar.className = 'member-avatar'; + fallback.className = 'member-fallback'; + fallback.textContent = initial(item.name) || 'S'; + avatar.appendChild(fallback); + if (item.avatar) { + var image = document.createElement('img'); + image.src = item.avatar; + image.alt = ''; + image.addEventListener('load', function () { + fallback.hidden = true; + }); + image.addEventListener('error', function () { + image.remove(); + fallback.hidden = false; + }); + avatar.appendChild(image); + } + return avatar; + } + + function replaceChildren(node, children) { + node.textContent = ''; + children.forEach(function (child) { + node.appendChild(child); + }); + } + + function renderProfile() { + $('profileName').textContent = mock.profile.name; + $('profileUID').textContent = t( + 'superadmin_center.uid_prefix', + 'UID: {id}' + ).replace('{id}', mock.profile.uid); + setImage( + $('profileAvatar'), + $('profileAvatarFallback'), + mock.profile.avatar, + mock.profile.name + ); + } + + function renderBalance() { + $('availableBalance').textContent = money(mock.balance.available); + } + + function summaryCell(label, value) { + var cell = document.createElement('div'); + var labelNode = document.createElement('span'); + var valueNode = document.createElement('strong'); + labelNode.textContent = label; + valueNode.textContent = value; + cell.appendChild(labelNode); + cell.appendChild(valueNode); + return cell; + } + + function activeItems() { + return state.activeTab === 'agency' ? mock.agencyList : mock.bdList; + } + + function renderTeamSummary() { + var cells = + state.activeTab === 'agency' + ? [ + summaryCell( + t('superadmin_center.agency_number', 'Agency number'), + count(mock.overview.totalAgency) + ), + summaryCell( + t('superadmin_center.hosts', 'Hosts'), + count(mock.overview.totalHosts) + ), + summaryCell( + t('superadmin_center.team_salary', 'Team salary'), + '$' + compactMoney(mock.overview.teamSalary) + ), + ] + : [ + summaryCell( + t('superadmin_center.bd_number', 'BD number'), + count(mock.overview.totalBD) + ), + summaryCell( + t('superadmin_center.agencies', 'Agencies'), + count(mock.overview.totalAgency) + ), + summaryCell( + t('superadmin_center.team_recharge', 'Team recharge'), + '$' + compactMoney(mock.overview.teamRecharge) + ), + ]; + replaceChildren($('teamSummary'), cells); + } + + function createMemberRow(item) { + var row = document.createElement('article'); + var main = document.createElement('div'); + var name = document.createElement('div'); + var meta = document.createElement('div'); + var side = document.createElement('div'); + var salary = document.createElement('strong'); + var sub = document.createElement('span'); + + row.className = 'member-row'; + main.className = 'member-main'; + name.className = 'member-name'; + meta.className = 'member-meta'; + side.className = 'member-side'; + + name.textContent = item.name; + meta.textContent = + state.activeTab === 'agency' + ? 'ID: ' + item.id + ' · BD: ' + item.bdId + : 'ID: ' + item.id; + salary.textContent = '$' + money(item.salary); + sub.textContent = + state.activeTab === 'agency' + ? count(item.hosts) + + ' ' + + t('superadmin_center.hosts', 'Hosts') + : count(item.agencies) + + ' ' + + t('superadmin_center.agencies', 'Agencies'); + + main.appendChild(name); + main.appendChild(meta); + side.appendChild(salary); + side.appendChild(sub); + row.appendChild(makeAvatar(item)); + row.appendChild(main); + row.appendChild(side); + row.addEventListener('click', function () { + state.selectedDetails = item; + renderDetails(); + openModal('detailsModal'); + }); + return row; + } + + function renderTeamList() { + document.querySelectorAll('[data-team-tab]').forEach(function (button) { + var active = + button.getAttribute('data-team-tab') === state.activeTab; + button.classList.toggle('is-active', active); + button.setAttribute('aria-selected', String(active)); + }); + renderTeamSummary(); + replaceChildren( + $('teamList'), + activeItems().map(function (item) { + return createMemberRow(item); + }) + ); + } + + function renderHistory() { + replaceChildren($('historySummary'), [ + summaryCell( + t('superadmin_center.total_income', 'Total income'), + '$' + money(mock.overview.teamSalary) + ), + summaryCell( + t('superadmin_center.previous_income', 'Previous income'), + '$' + money(mock.history[1].totalSalary) + ), + ]); + replaceChildren( + $('historyList'), + mock.history.map(function (item) { + var row = document.createElement('article'); + var title = document.createElement('div'); + var status = document.createElement('span'); + var grid = document.createElement('div'); + row.className = 'history-row'; + title.className = 'history-title'; + status.className = 'status-tag'; + grid.className = 'history-grid'; + title.textContent = item.month; + status.textContent = item.status; + grid.appendChild( + summaryCell( + t('superadmin_center.bd_number', 'BD number'), + count(item.bdCount) + ) + ); + grid.appendChild( + summaryCell( + t('superadmin_center.agency_number', 'Agency number'), + count(item.agencyCount) + ) + ); + grid.appendChild( + summaryCell( + t('superadmin_center.team_salary', 'Team salary'), + '$' + compactMoney(item.totalSalary) + ) + ); + row.appendChild(status); + row.appendChild(title); + row.appendChild(grid); + return row; + }) + ); + } + + function renderDetails() { + var item = state.selectedDetails; + if (!item) return; + $('detailsTitle').textContent = item.name; + replaceChildren($('detailsSummary'), [ + summaryCell('ID', item.id), + summaryCell( + t('superadmin_center.team_salary', 'Team salary'), + '$' + money(item.salary) + ), + summaryCell( + t('superadmin_center.team_recharge', 'Team recharge'), + '$' + money(item.recharge) + ), + ]); + replaceChildren($('detailsList'), [ + createReadonlyLine( + state.activeTab === 'agency' + ? t('superadmin_center.hosts', 'Hosts') + : t('superadmin_center.agencies', 'Agencies'), + count(state.activeTab === 'agency' ? item.hosts : item.agencies) + ), + ]); + } + + function createReadonlyLine(label, value) { + var row = document.createElement('div'); + var labelNode = document.createElement('span'); + var valueNode = document.createElement('strong'); + row.className = 'readonly-line'; + labelNode.textContent = label; + valueNode.textContent = value; + row.appendChild(labelNode); + row.appendChild(valueNode); + return row; + } + + function inviteTitle() { + return state.inviteMode === 'agent' + ? t('superadmin_center.invite_agent', 'Invite Agent') + : t('superadmin_center.invite_bd', 'Invite BD'); + } + + function inviteRecords() { + if (!state.inviteQuery) return mock.inviteRecords[state.inviteMode]; + var keyword = state.inviteQuery.toLowerCase(); + return mock.searchPool[state.inviteMode].filter(function (item) { + return ( + item.name.toLowerCase().indexOf(keyword) >= 0 || + item.account.indexOf(keyword) >= 0 || + item.id.indexOf(keyword) >= 0 + ); + }); + } + + function renderInvite() { + $('inviteModalTitle').textContent = inviteTitle(); + var status = $('inviteStatus'); + status.hidden = !state.inviteStatus; + status.textContent = state.inviteStatus; + status.className = + 'invite-status' + + (state.inviteStatusType ? ' ' + state.inviteStatusType : ''); + + var records = inviteRecords(); + $('inviteEmpty').hidden = records.length > 0; + replaceChildren( + $('inviteList'), + records.map(function (record) { + var profile = record.profile || record; + var card = document.createElement('article'); + var main = document.createElement('div'); + var name = document.createElement('div'); + var meta = document.createElement('div'); + var action = document.createElement('button'); + card.className = 'invite-card'; + main.className = 'invite-main'; + name.className = 'invite-name'; + meta.className = 'invite-meta'; + action.className = 'invite-action'; + action.type = 'button'; + name.textContent = profile.name; + meta.textContent = 'ID: ' + profile.account; + action.textContent = + record.status || t('superadmin_center.invite', 'Invite'); + action.disabled = !!record.status; + action.addEventListener('click', function () { + state.inviteStatus = t( + 'superadmin_center.invite_success', + 'Invitation submitted.' + ); + state.inviteStatusType = 'success'; + renderInvite(); + }); + main.appendChild(name); + main.appendChild(meta); + card.appendChild(makeAvatar(profile)); + card.appendChild(main); + card.appendChild(action); + return card; + }) + ); + } + + function openModal(id) { + var modal = $(id); + if (!modal) return; + modal.hidden = false; + modal.setAttribute('aria-hidden', 'false'); + document.body.classList.add('modal-open'); + } + + function closeModal(id) { + var modal = $(id); + if (!modal) return; + modal.hidden = true; + modal.setAttribute('aria-hidden', 'true'); + var hasOpen = Array.prototype.some.call( + document.querySelectorAll('.sheet-modal, .invite-modal'), + function (node) { + return !node.hidden; + } + ); + document.body.classList.toggle('modal-open', hasOpen); + } + + function navigateToWithdrawExchange() { + var params = new URLSearchParams(window.location.search); + params.set('identity', 'SUPER_ADMIN'); + params.set('salaryType', 'SUPER_ADMIN_SALARY'); + if (!params.has('mode')) params.set('mode', 'withdraw'); + window.location.href = + '../withdraw-exchange/index.html?' + params.toString(); + } + + function bindEvents() { + $('backButton').addEventListener('click', function () { + if (window.HyAppBridge) window.HyAppBridge.back(); + }); + $('withdrawExchangeButton').addEventListener( + 'click', + navigateToWithdrawExchange + ); + document.querySelectorAll('.menu-row').forEach(function (button) { + button.addEventListener('click', function () { + var action = button.getAttribute('data-action'); + if (action === 'history') { + renderHistory(); + openModal('historyModal'); + return; + } + if (action === 'invite-bd' || action === 'invite-agent') { + state.inviteMode = + action === 'invite-agent' ? 'agent' : 'bd'; + state.inviteQuery = ''; + state.inviteStatus = ''; + state.inviteStatusType = ''; + $('inviteSearchInput').value = ''; + renderInvite(); + openModal('inviteModal'); + } + }); + }); + document.querySelectorAll('[data-team-tab]').forEach(function (button) { + button.addEventListener('click', function () { + state.activeTab = button.getAttribute('data-team-tab') || 'bd'; + renderTeamList(); + }); + }); + document + .querySelectorAll('[data-close-modal]') + .forEach(function (node) { + node.addEventListener('click', function () { + closeModal(node.getAttribute('data-close-modal')); + }); + }); + $('inviteSearchForm').addEventListener('submit', function (event) { + event.preventDefault(); + state.inviteQuery = $('inviteSearchInput').value.trim(); + state.inviteStatus = state.inviteQuery + ? '' + : t('superadmin_center.enter_user_id', 'Enter User ID'); + state.inviteStatusType = state.inviteQuery ? '' : 'warning'; + renderInvite(); + }); + document.addEventListener('keydown', function (event) { + if (event.key !== 'Escape') return; + closeModal('historyModal'); + closeModal('detailsModal'); + closeModal('inviteModal'); + }); + } + + function render() { + renderProfile(); + renderBalance(); + renderTeamList(); + } + + function init() { + render(); + bindEvents(); + if (window.HyAppBridge) { + window.HyAppBridge.ready({ page: 'superadmin-center', mock: true }); + } + } + + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + } else { + init(); + } + + window.addEventListener('hyapp:i18n-ready', function () { + render(); + if (!$('historyModal').hidden) renderHistory(); + if (!$('inviteModal').hidden) renderInvite(); + if (!$('detailsModal').hidden) renderDetails(); + }); +})(); diff --git a/gonghui/superadmin-center/style.css b/gonghui/superadmin-center/style.css new file mode 100644 index 0000000..ee385e4 --- /dev/null +++ b/gonghui/superadmin-center/style.css @@ -0,0 +1,535 @@ +.role-pill { + display: inline-flex; + align-items: center; + min-height: 24px; + padding: 0 10px; + border-radius: 999px; + background: var(--hy-theme-primary-soft, #f4ebff); + color: var(--primary-deep); + font-size: 12px; + font-weight: 900; +} + +.superadmin-center .name { + margin-top: 7px; +} + +.salary-card, +.team-card { + padding: 16px; +} + +.salary-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; +} + +.salary-head h2 { + margin: 0; + color: var(--text); + font-size: 18px; + font-weight: 950; + line-height: 1.2; +} + +.salary-value { + display: flex; + align-items: center; + gap: 10px; + margin-top: 18px; + color: #23272d; + font-size: 34px; + font-weight: 950; + line-height: 1; +} + +.coin-icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 28px; + height: 28px; + flex: 0 0 28px; + border-radius: 50%; + background: linear-gradient(145deg, #ffe179 0%, #ffb62d 52%, #f49b20 100%); + color: #fff; + font-size: 19px; + font-weight: 950; + box-shadow: + inset 0 1px 2px rgba(255, 255, 255, 0.7), + 0 1px 2px rgba(195, 117, 12, 0.24); +} + +.salary-value strong { + min-width: 0; + overflow-wrap: anywhere; +} + +.menu-card { + display: grid; + padding: 0 16px; +} + +.menu-row { + display: grid; + grid-template-columns: 38px minmax(0, 1fr) 24px; + gap: 12px; + align-items: center; + min-height: 64px; + border-bottom: 1px solid var(--line); + text-align: left; +} + +.menu-row:last-child { + border-bottom: 0; +} + +.menu-icon { + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + border-radius: 50%; + background: var(--primary-soft); + color: var(--primary-deep); +} + +.menu-icon svg { + width: 20px; + height: 20px; +} + +.menu-icon path { + fill: none; + stroke: currentColor; + stroke-linecap: round; + stroke-linejoin: round; + stroke-width: 2; +} + +.menu-label { + min-width: 0; + overflow: hidden; + color: #464951; + font-size: 16px; + font-weight: 850; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; +} + +.team-card { + display: grid; + gap: 12px; +} + +.team-tabs { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 6px; + padding: 6px; + border-radius: 8px; + background: var(--hy-theme-primary-soft, #f4ebff); +} + +.team-tab { + display: flex; + align-items: center; + justify-content: center; + min-height: 42px; + border-radius: 7px; + color: var(--muted); + font-size: 15px; + font-weight: 900; +} + +.team-tab.is-active { + background: #fff; + color: var(--text); + box-shadow: 0 8px 18px rgba(98, 72, 155, 0.08); +} + +.team-summary, +.history-summary { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; +} + +.history-summary.compact { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} + +.team-summary > div, +.history-summary > div { + min-width: 0; + padding: 10px; + border-radius: 8px; + background: var(--hy-theme-primary-soft, #f4ebff); +} + +.team-summary span, +.history-summary span { + display: block; + overflow: hidden; + color: var(--muted); + font-size: 11px; + font-weight: 850; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; +} + +.team-summary strong, +.history-summary strong { + display: block; + margin-top: 5px; + overflow-wrap: anywhere; + color: var(--text); + font-size: 15px; + font-weight: 950; + line-height: 1.15; +} + +.team-list, +.history-list, +.invite-list { + display: grid; + gap: 10px; +} + +.team-list { + max-height: 360px; + overflow-y: auto; + -webkit-overflow-scrolling: touch; +} + +.member-row, +.invite-card { + display: grid; + grid-template-columns: 44px minmax(0, 1fr) auto; + gap: 10px; + align-items: center; + padding: 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfcfc; + text-align: left; +} + +.member-avatar { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + overflow: hidden; + border-radius: 50%; + background: var(--primary); +} + +.member-avatar img { + display: block; + width: 100%; + height: 100%; + object-fit: cover; +} + +.member-fallback { + color: var(--primary-deep); + font-size: 18px; + font-weight: 950; +} + +.member-main, +.invite-main { + min-width: 0; +} + +.member-name, +.invite-name { + overflow: hidden; + color: var(--text); + font-size: 15px; + font-weight: 900; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; +} + +.member-meta, +.invite-meta { + margin-top: 4px; + overflow: hidden; + color: var(--muted); + font-size: 12px; + font-weight: 800; + line-height: 1.2; + text-overflow: ellipsis; + white-space: nowrap; +} + +.member-side { + display: grid; + gap: 3px; + color: #555b64; + font-size: 12px; + font-weight: 850; + line-height: 1.2; + text-align: right; +} + +.member-side strong { + color: var(--primary-deep); + font-size: 14px; + font-weight: 950; +} + +.history-sheet { + max-height: 82vh; + max-height: 82dvh; +} + +.history-summary, +.history-list { + padding: 0 18px 14px; +} + +.history-row { + display: grid; + gap: 9px; + padding: 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfcfc; +} + +.history-title { + color: var(--text); + font-size: 16px; + font-weight: 950; +} + +.status-tag { + justify-self: flex-start; + padding: 5px 9px; + border-radius: 999px; + background: var(--primary-soft); + color: var(--primary-deep); + font-size: 12px; + font-weight: 900; +} + +.history-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 8px; +} + +.readonly-line { + display: flex; + align-items: center; + justify-content: space-between; + min-height: 48px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfcfc; + color: var(--muted); + font-size: 14px; + font-weight: 850; +} + +.readonly-line strong { + color: var(--text); + font-weight: 950; +} + +.invite-modal { + position: fixed; + z-index: 24; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + padding: 18px; +} + +.invite-dialog { + position: relative; + display: flex; + flex-direction: column; + width: min(394px, 100%); + max-height: 80vh; + max-height: 80dvh; + overflow: hidden; + border-radius: 14px; + background: #fff; + box-shadow: 0 18px 44px rgba(23, 31, 42, 0.2); +} + +.invite-head { + display: flex; + align-items: center; + justify-content: space-between; + gap: 12px; + padding: 18px 18px 12px; +} + +.invite-head h2 { + margin: 0; + color: var(--text); + font-size: 20px; + font-weight: 950; +} + +.invite-close { + display: flex; + align-items: center; + justify-content: center; + width: 34px; + height: 34px; + flex: 0 0 34px; + border-radius: 50%; + background: #f1f4f4; + color: #5d6570; + font-size: 25px; + font-weight: 400; + line-height: 1; +} + +.invite-body { + display: grid; + gap: 12px; + min-height: 0; + overflow-y: auto; + padding: 0 18px 18px; + -webkit-overflow-scrolling: touch; +} + +.invite-field { + display: grid; + gap: 8px; +} + +.invite-field > span { + color: var(--text); + font-size: 14px; + font-weight: 900; +} + +.invite-search-control { + display: grid; + grid-template-columns: minmax(0, 1fr) 96px; + gap: 8px; +} + +.invite-search-control input { + min-width: 0; + height: 44px; + padding: 0 12px; + border: 1px solid var(--line); + border-radius: 8px; + background: #fbfcfc; + color: var(--text); + font-size: 14px; + font-weight: 800; +} + +.invite-search-control button, +.invite-action { + display: flex; + align-items: center; + justify-content: center; + min-height: 44px; + border-radius: 8px; + background: var(--button); + color: #fff; + font-size: 14px; + font-weight: 950; +} + +.invite-action { + min-width: 82px; + padding: 0 12px; +} + +.invite-action:disabled { + background: var(--hy-theme-button-disabled, #eadffc); + color: var(--hy-theme-button-disabled-text, #9a8faa); +} + +.invite-status, +.invite-empty { + min-height: 42px; + padding: 11px 12px; + border-radius: 8px; + background: var(--primary-soft); + color: var(--primary-deep); + font-size: 13px; + font-weight: 900; +} + +.invite-status.warning { + background: #fff5e8; + color: #ba791c; +} + +.invite-status.success { + background: #ecfbef; + color: #219251; +} + +.invite-empty { + display: flex; + align-items: center; + justify-content: center; + color: var(--muted); +} + +body.modal-open { + overflow: hidden; +} + +[dir='rtl'] .menu-row, +[dir='rtl'] .member-row, +[dir='rtl'] .invite-card { + text-align: right; +} + +[dir='rtl'] .member-side { + text-align: left; +} + +[dir='rtl'] .chevron { + transform: rotate(180deg); +} + +@media (max-width: 360px) { + .salary-card, + .team-card { + padding-right: 14px; + padding-left: 14px; + } + + .team-summary, + .history-summary, + .history-grid { + grid-template-columns: 1fr; + } + + .member-row, + .invite-card { + grid-template-columns: 40px minmax(0, 1fr); + } + + .member-side, + .invite-action { + grid-column: 2; + justify-self: start; + text-align: left; + } +}