公会相关

This commit is contained in:
zhx 2026-06-04 19:29:48 +08:00
parent 0d6d3feb1e
commit dcc1938e63
17 changed files with 2691 additions and 141 deletions

View File

@ -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,
};

View File

@ -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": "الهدف",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -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",

View File

@ -65,8 +65,31 @@
</div>
<div class="profile-copy">
<div class="name" id="agencyName">Yumi Star Agency</div>
<div class="meta" id="agencyUid">UID: 163003</div>
<div class="agency-line" id="bdName">BD: Mila</div>
<div class="meta" id="agencyShortId">ID: 163003</div>
<div class="bd-row" id="bdRow" hidden>
<span
class="bd-label"
data-i18n="agency_center.bd_label"
>BD:</span
>
<span class="bd-avatar-shell">
<img
class="bd-avatar-image"
id="bdAvatar"
alt=""
/>
<span
class="bd-avatar-fallback"
id="bdAvatarFallback"
aria-hidden="true"
hidden
>B</span
>
</span>
<span class="bd-short-id" id="bdShortId"
>880001</span
>
</div>
</div>
<button
class="message-button"

View File

@ -5,9 +5,13 @@
var mock = {
profile: {
name: 'Yumi Star Agency',
uid: '163003',
shortId: '163003',
avatar: avatarData('A', '#dbc8ff', '#7d57c7'),
bd: 'Mila BD (880001)',
bd: {
name: 'Mila BD',
shortId: '880001',
avatar: avatarData('M', '#efe4ff', '#7d57c7'),
},
},
metrics: {
salary: 12880.75,
@ -154,13 +158,13 @@
function salaryDisplayAmount(salary, assetType) {
if (!salary) return 0;
if (salary.display_amount !== undefined && salary.display_amount !== null) {
if (
salary.display_amount !== undefined &&
salary.display_amount !== null
) {
return toNumber(salary.display_amount);
}
if (
assetType === AGENCY_SALARY_USD ||
assetType === HOST_SALARY_USD
) {
if (assetType === AGENCY_SALARY_USD || assetType === HOST_SALARY_USD) {
return toNumber(salary.available_amount) / 100;
}
return toNumber(salary.available_amount);
@ -211,11 +215,7 @@
}
function hasRealAPI() {
return (
!state.isMock &&
window.HyAppAPI &&
window.HyAppAPI.agencyCenter
);
return !state.isMock && window.HyAppAPI && window.HyAppAPI.agencyCenter;
}
function displayName(user, fallback) {
@ -234,12 +234,15 @@
var bd = payload && payload.bd ? payload.bd : null;
state.profile = {
name: agency.name || mock.profile.name,
uid: agency.agency_id || mock.profile.uid,
shortId: agency.agency_id || mock.profile.shortId,
avatar: agency.avatar || '',
bd: bd
? displayName(bd, '') +
(bd.display_user_id ? ' (' + bd.display_user_id + ')' : '')
: '',
? {
name: displayName(bd, ''),
shortId: bd.display_user_id || bd.user_id || '',
avatar: bd.avatar || '',
}
: null,
};
state.salary = salaryDisplayAmount(
payload ? payload.salary : null,
@ -311,9 +314,21 @@
function renderProfile() {
var profile = state.profile || mock.profile;
var bd = profile.bd || null;
var bdRow = $('bdRow');
$('agencyName').textContent = profile.name;
$('agencyUid').textContent = 'UID: ' + profile.uid;
$('bdName').textContent = profile.bd ? 'BD: ' + profile.bd : '';
$('agencyShortId').textContent = t(
'agency_center.agency_short_id',
'ID: {id}'
).replace('{id}', profile.shortId || '-');
if (bd && bd.shortId) {
bdRow.hidden = false;
$('bdShortId').textContent = bd.shortId;
setImage($('bdAvatar'), $('bdAvatarFallback'), bd.avatar, bd.name);
} else {
bdRow.hidden = true;
$('bdShortId').textContent = '';
}
setImage(
$('agencyAvatar'),
$('agencyAvatarFallback'),

View File

@ -247,6 +247,7 @@ input:focus-visible {
}
.profile-copy {
flex: 1 1 auto;
min-width: 0;
margin-left: 20px;
}
@ -261,8 +262,7 @@ input:focus-visible {
white-space: nowrap;
}
.meta,
.agency-line {
.meta {
overflow: hidden;
color: var(--muted);
font-size: 13px;
@ -276,8 +276,58 @@ input:focus-visible {
margin-top: 6px;
}
.agency-line {
.bd-row {
display: inline-flex;
align-items: center;
max-width: 100%;
margin-top: 7px;
overflow: hidden;
color: var(--muted);
font-size: 13px;
font-weight: 800;
line-height: 1.2;
white-space: nowrap;
}
.bd-label {
flex: 0 0 auto;
margin-right: 5px;
}
.bd-avatar-shell {
position: relative;
width: 18px;
height: 18px;
flex: 0 0 18px;
margin-right: 5px;
}
.bd-avatar-image,
.bd-avatar-fallback {
width: 18px;
height: 18px;
border-radius: 50%;
}
.bd-avatar-image {
display: block;
object-fit: cover;
}
.bd-avatar-fallback {
display: flex;
align-items: center;
justify-content: center;
background: var(--primary-soft);
color: var(--primary-deep);
font-size: 10px;
font-weight: 950;
}
.bd-short-id {
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
}
.message-button {
@ -886,6 +936,10 @@ input:focus-visible {
text-align: right;
}
[dir='rtl'] .bd-row {
direction: ltr;
}
[dir='rtl'] .message-button {
margin-right: auto;
margin-left: 0;

View File

@ -74,7 +74,8 @@ input:focus-visible {
.host-center,
.bd-center,
.agency-center {
.agency-center,
.superadmin-center {
position: relative;
width: 100%;
max-width: 430px;

View File

@ -9,7 +9,7 @@
<title>Host Center</title>
<link rel="stylesheet" href="../../common/theme.css?v=20260602" />
<link rel="stylesheet" href="../common/center.css?v=20260602" />
<link rel="stylesheet" href="./style.css?v=20260602" />
<link rel="stylesheet" href="./style.css?v=20260604-diamonds-label" />
</head>
<body>
@ -66,8 +66,8 @@
</div>
</div>
<div class="profile-copy">
<div class="name" id="profileName">Yumi_Host</div>
<div class="meta" id="profileUID">UID: 1688008</div>
<div class="name" id="profileName"></div>
<div class="meta" id="profileUID"></div>
<div class="profile-badge" id="profileBadge"></div>
</div>
</section>
@ -112,7 +112,7 @@
</div>
<div class="task-item">
<span data-i18n="host_center.next_level_need"
>Needed for next level</span
>Needed diamonds for next level</span
>
<strong id="nextLevelNeed"></strong>
</div>
@ -147,14 +147,61 @@
</section>
</main>
<div
class="sheet-modal"
id="policyModal"
hidden
aria-hidden="true"
>
<button
class="modal-backdrop"
type="button"
data-close-policy
aria-label="Close"
></button>
<section
class="bottom-sheet policy-sheet"
role="dialog"
aria-modal="true"
aria-labelledby="policyTitle"
>
<div class="sheet-head">
<h2 id="policyTitle">Platform policy</h2>
<button
class="sheet-close"
type="button"
data-close-policy
aria-label="Close"
>
&times;
</button>
</div>
<div class="policy-sheet-body">
<div class="policy-summary">
<strong id="policyName">Current policy</strong>
<span id="policyMeta"></span>
</div>
<div
class="policy-empty"
id="policyEmpty"
hidden
></div>
<div
class="policy-level-list"
id="policyLevelList"
></div>
</div>
</section>
</div>
<div class="home-indicator" aria-hidden="true"></div>
</div>
<script src="../../common/api.js?v=20260602-api"></script>
<script src="../../common/api.js?v=20260604-host-agency"></script>
<script src="../../common/toast.js"></script>
<script src="../../common/params.js?v=20260602-api"></script>
<script src="../../common/jsbridge.js"></script>
<script src="../../common/i18n.js?v=20260602-api"></script>
<script src="./script.js?v=20260602-api"></script>
<script src="../../common/i18n.js?v=20260604-diamonds-label"></script>
<script src="./script.js?v=20260604-no-zero-scroll"></script>
</body>
</html>

View File

@ -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,
});
}
});

View File

@ -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;
}
}

View File

@ -0,0 +1,362 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover"
/>
<title>Superadmin Center</title>
<link rel="stylesheet" href="../../common/theme.css" />
<link
rel="stylesheet"
href="../common/center.css?v=20260604-superadmin"
/>
<link rel="stylesheet" href="./style.css?v=20260604" />
</head>
<body>
<div class="superadmin-center" aria-label="Superadmin Center">
<div class="hero-bg" aria-hidden="true"></div>
<nav class="title-bar">
<button
class="back-button"
id="backButton"
type="button"
aria-label="Back"
data-i18n-aria="superadmin_center.back"
>
<svg viewBox="0 0 32 32" aria-hidden="true">
<path d="M20 8 12 16l8 8" />
</svg>
</button>
<h1 data-i18n="superadmin_center.title">Superadmin Center</h1>
<div class="language-switcher">
<button
class="language-button"
type="button"
aria-label="Change language"
aria-expanded="false"
data-language-toggle
data-current-lang
data-i18n-aria="superadmin_center.change_language"
>
EN
</button>
<div class="language-menu" data-language-menu hidden>
<button type="button" data-lang-option="en">EN</button>
<button type="button" data-lang-option="ar">AR</button>
<button type="button" data-lang-option="tr">TR</button>
<button type="button" data-lang-option="es">ES</button>
<button type="button" data-lang-option="zh">ZH</button>
<button type="button" data-lang-option="id">ID</button>
</div>
</div>
</nav>
<main class="content">
<section class="card profile-card">
<div class="avatar-shell">
<img class="avatar-image" id="profileAvatar" alt="" />
<div
class="avatar-fallback"
id="profileAvatarFallback"
aria-hidden="true"
hidden
>
S
</div>
</div>
<div class="profile-copy">
<div
class="role-pill"
data-i18n="superadmin_center.role"
>
Super Admin
</div>
<div class="name" id="profileName">-</div>
<div class="meta" id="profileUID">UID: -</div>
</div>
</section>
<section class="card salary-card">
<div class="salary-head">
<h2 data-i18n="superadmin_center.salary">Salary</h2>
<button
class="text-link"
id="withdrawExchangeButton"
type="button"
data-i18n="superadmin_center.withdraw_exchange"
>
Withdraw/Exchange
</button>
</div>
<div class="salary-value">
<span class="coin-icon" aria-hidden="true">$</span>
<strong id="availableBalance">0.00</strong>
</div>
</section>
<section class="card menu-card" aria-label="Superadmin tools">
<button
class="menu-row"
type="button"
data-action="history"
>
<span class="menu-icon" aria-hidden="true">
<svg viewBox="0 0 24 24">
<path d="M12 4a8 8 0 1 1-7.2 4.5" />
<path d="M5 4v5h5" />
<path d="M12 8v5l3 2" />
</svg>
</span>
<span
class="menu-label"
data-i18n="superadmin_center.salary_history"
>Salary History</span
>
<span class="chevron" aria-hidden="true"></span>
</button>
<button
class="menu-row"
type="button"
data-action="invite-bd"
>
<span class="menu-icon" aria-hidden="true">
<svg viewBox="0 0 24 24">
<path d="M10 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
<path
d="M3 21c.9-4.7 3.2-7 7-7 2 0 3.7.7 5 2"
/>
<path d="M19 9v8" />
<path d="M15 13h8" />
</svg>
</span>
<span
class="menu-label"
data-i18n="superadmin_center.invite_bd"
>Invite BD</span
>
<span class="chevron" aria-hidden="true"></span>
</button>
<button
class="menu-row"
type="button"
data-action="invite-agent"
>
<span class="menu-icon" aria-hidden="true">
<svg viewBox="0 0 24 24">
<path d="M9 11a4 4 0 1 0 0-8 4 4 0 0 0 0 8Z" />
<path
d="M2.5 21c.8-4.3 3.3-6.4 6.5-6.4 2 0 3.6.7 4.8 2"
/>
<path d="M17 11h6" />
<path d="M20 8v6" />
</svg>
</span>
<span
class="menu-label"
data-i18n="superadmin_center.invite_agent"
>Invite Agent</span
>
<span class="chevron" aria-hidden="true"></span>
</button>
</section>
<section class="card team-card">
<div
class="team-tabs"
role="tablist"
aria-label="Team list"
>
<button
class="team-tab is-active"
type="button"
role="tab"
aria-selected="true"
data-team-tab="bd"
data-i18n="superadmin_center.bd_list"
>
BD List
</button>
<button
class="team-tab"
type="button"
role="tab"
aria-selected="false"
data-team-tab="agency"
data-i18n="superadmin_center.agency_list"
>
Agency List
</button>
</div>
<div class="team-summary" id="teamSummary"></div>
<div class="team-list" id="teamList"></div>
</section>
</main>
<div class="home-indicator" aria-hidden="true"></div>
<div
class="sheet-modal"
id="historyModal"
hidden
aria-hidden="true"
>
<button
class="modal-backdrop"
type="button"
data-close-modal="historyModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
></button>
<section
class="bottom-sheet history-sheet"
role="dialog"
aria-modal="true"
aria-labelledby="historyTitle"
>
<div class="sheet-head">
<h2
id="historyTitle"
data-i18n="superadmin_center.salary_history"
>
Salary History
</h2>
<button
class="sheet-close"
type="button"
data-close-modal="historyModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
>
×
</button>
</div>
<div class="history-summary" id="historySummary"></div>
<div class="history-list" id="historyList"></div>
</section>
</div>
<div
class="sheet-modal"
id="detailsModal"
hidden
aria-hidden="true"
>
<button
class="modal-backdrop"
type="button"
data-close-modal="detailsModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
></button>
<section
class="bottom-sheet history-sheet"
role="dialog"
aria-modal="true"
aria-labelledby="detailsTitle"
>
<div class="sheet-head">
<h2 id="detailsTitle">-</h2>
<button
class="sheet-close"
type="button"
data-close-modal="detailsModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
>
×
</button>
</div>
<div
class="history-summary compact"
id="detailsSummary"
></div>
<div class="history-list" id="detailsList"></div>
</section>
</div>
<div
class="invite-modal"
id="inviteModal"
hidden
aria-hidden="true"
>
<button
class="modal-backdrop"
type="button"
data-close-modal="inviteModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
></button>
<section
class="invite-dialog"
role="dialog"
aria-modal="true"
aria-labelledby="inviteModalTitle"
>
<div class="invite-head">
<h2 id="inviteModalTitle">Invite BD</h2>
<button
class="invite-close"
type="button"
data-close-modal="inviteModal"
aria-label="Close"
data-i18n-aria="superadmin_center.close"
>
×
</button>
</div>
<div class="invite-body">
<form class="invite-search-form" id="inviteSearchForm">
<label class="invite-field">
<span
data-i18n="superadmin_center.enter_user_id"
>Enter User ID</span
>
<div class="invite-search-control">
<input
id="inviteSearchInput"
type="text"
inputmode="text"
autocomplete="off"
placeholder="Enter User ID"
data-i18n-placeholder="superadmin_center.enter_user_id"
/>
<button
id="inviteSearchButton"
type="submit"
data-i18n="superadmin_center.search"
>
Search
</button>
</div>
</label>
</form>
<div
class="invite-status"
id="inviteStatus"
role="status"
hidden
></div>
<div class="invite-list" id="inviteList"></div>
<div
class="invite-empty"
id="inviteEmpty"
data-i18n="superadmin_center.no_users_found"
>
No users found
</div>
</div>
</section>
</div>
</div>
<script src="../../common/toast.js"></script>
<script src="../../common/jsbridge.js"></script>
<script src="../../common/i18n.js?v=20260604"></script>
<script src="./script.js?v=20260604"></script>
</body>
</html>

View File

@ -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 =
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">' +
'<rect width="96" height="96" rx="48" fill="' +
background +
'"/>' +
'<text x="50%" y="54%" text-anchor="middle" dominant-baseline="middle" fill="' +
foreground +
'" font-family="Arial, sans-serif" font-size="38" font-weight="800">' +
letter +
'</text>' +
'</svg>';
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();
});
})();

View File

@ -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;
}
}