593 lines
20 KiB
JavaScript
593 lines
20 KiB
JavaScript
(function () {
|
|
var FIRST_BILL_MONTH = '2026-06';
|
|
var mock = {
|
|
profile: {
|
|
name: 'BD_Jasmine',
|
|
uid: 'BD-163008',
|
|
avatar: avatarData('B', '#dbc8ff', '#7d57c7'),
|
|
},
|
|
balance: {
|
|
available: 48620.8,
|
|
},
|
|
billMonth: currentMonth(),
|
|
agencyBill: {
|
|
billTitle: '2026.06',
|
|
agencyNumber: 12,
|
|
totalSalary: 128450.35,
|
|
totalRecharge: 356900.0,
|
|
memberBillList: [
|
|
{
|
|
name: 'Yumi Star Agency',
|
|
account: '163003',
|
|
avatar: avatarData('Y', '#dbc8ff', '#7d57c7'),
|
|
teamSalaryAmount: 38420.55,
|
|
teamRechargeAmount: 126880,
|
|
teamMemberCount: 28,
|
|
},
|
|
{
|
|
name: 'Moon Live Agency',
|
|
account: '163086',
|
|
avatar: avatarData('M', '#f4ebff', '#7d57c7'),
|
|
teamSalaryAmount: 29106.2,
|
|
teamRechargeAmount: 98540,
|
|
teamMemberCount: 19,
|
|
},
|
|
{
|
|
name: 'Nova Host Guild',
|
|
account: '163221',
|
|
avatar: avatarData('N', '#dbc8ff', '#7d57c7'),
|
|
teamSalaryAmount: 22450,
|
|
teamRechargeAmount: 73150,
|
|
teamMemberCount: 16,
|
|
},
|
|
],
|
|
},
|
|
inviteRecords: [
|
|
{
|
|
id: 'inv-001',
|
|
status: 'pending',
|
|
profile: {
|
|
id: '3187001',
|
|
name: 'Agent_Lina',
|
|
account: '168801',
|
|
avatar: avatarData('L', '#dbc8ff', '#7d57c7'),
|
|
},
|
|
},
|
|
{
|
|
id: 'inv-002',
|
|
status: 'success',
|
|
profile: {
|
|
id: '3187002',
|
|
name: 'Agent_Omar',
|
|
account: '168802',
|
|
avatar: avatarData('O', '#f4ebff', '#7d57c7'),
|
|
},
|
|
},
|
|
],
|
|
searchPool: [
|
|
{
|
|
id: '3187099',
|
|
name: 'Agent_River',
|
|
account: '168899',
|
|
avatar: avatarData('R', '#dbc8ff', '#7d57c7'),
|
|
},
|
|
{
|
|
id: '3187100',
|
|
name: 'Agent_Sara',
|
|
account: '168900',
|
|
avatar: avatarData('S', '#f4ebff', '#7d57c7'),
|
|
},
|
|
],
|
|
};
|
|
|
|
var state = {
|
|
billMonth: normalizeBillMonth(mock.billMonth),
|
|
inviteQuery: '',
|
|
inviteMode: 'records',
|
|
inviteStatus: '',
|
|
inviteStatusType: '',
|
|
searched: [],
|
|
};
|
|
|
|
function $(id) {
|
|
return document.getElementById(id);
|
|
}
|
|
|
|
function t(key, fallback) {
|
|
return window.HyAppI18n && window.HyAppI18n.t
|
|
? window.HyAppI18n.t(key, fallback)
|
|
: fallback || key;
|
|
}
|
|
|
|
function avatarData(text, background, foreground) {
|
|
var letter = encodeURIComponent(String(text || 'B').charAt(0));
|
|
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 currentMonth() {
|
|
var now = new Date();
|
|
return (
|
|
String(now.getFullYear()) +
|
|
'-' +
|
|
String(now.getMonth() + 1).padStart(2, '0')
|
|
);
|
|
}
|
|
|
|
function monthDate(value) {
|
|
var parts = String(value || currentMonth()).split('-');
|
|
var year = Number(parts[0]) || new Date().getFullYear();
|
|
var month = Number(parts[1]) || new Date().getMonth() + 1;
|
|
return new Date(year, month - 1, 1);
|
|
}
|
|
|
|
function monthValue(date) {
|
|
return (
|
|
String(date.getFullYear()) +
|
|
'-' +
|
|
String(date.getMonth() + 1).padStart(2, '0')
|
|
);
|
|
}
|
|
|
|
function monthTime(value) {
|
|
return monthDate(value).getTime();
|
|
}
|
|
|
|
function normalizeBillMonth(value) {
|
|
if (monthTime(value) < monthTime(FIRST_BILL_MONTH)) {
|
|
return FIRST_BILL_MONTH;
|
|
}
|
|
if (monthTime(value) > monthTime(currentMonth())) {
|
|
return currentMonth();
|
|
}
|
|
return monthValue(monthDate(value));
|
|
}
|
|
|
|
function monthLabel(value) {
|
|
var date = monthDate(value);
|
|
return (
|
|
String(date.getFullYear()) +
|
|
'年' +
|
|
String(date.getMonth() + 1).padStart(2, '0') +
|
|
'月'
|
|
);
|
|
}
|
|
|
|
function monthOptions() {
|
|
var options = [];
|
|
var cursor = monthDate(currentMonth());
|
|
var first = monthDate(FIRST_BILL_MONTH);
|
|
while (cursor.getTime() >= first.getTime()) {
|
|
options.push(new Date(cursor.getFullYear(), cursor.getMonth(), 1));
|
|
cursor = new Date(cursor.getFullYear(), cursor.getMonth() - 1, 1);
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function money(value) {
|
|
var number = Number(value || 0);
|
|
return number.toLocaleString('en-US', {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
});
|
|
}
|
|
|
|
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 (window.HyAppToast) {
|
|
window.HyAppToast.show(message);
|
|
return;
|
|
}
|
|
window.alert(message);
|
|
}
|
|
|
|
function replaceChildren(node, children) {
|
|
node.textContent = '';
|
|
children.forEach(function (child) {
|
|
node.appendChild(child);
|
|
});
|
|
}
|
|
|
|
function renderImageAvatar(container, src, name, fallbackClass) {
|
|
container.textContent = '';
|
|
if (!src) {
|
|
var fallback = document.createElement('span');
|
|
fallback.className = fallbackClass || 'member-fallback';
|
|
fallback.textContent = initial(name) || 'B';
|
|
container.appendChild(fallback);
|
|
return;
|
|
}
|
|
var image = document.createElement('img');
|
|
image.src = src;
|
|
image.alt = '';
|
|
image.addEventListener('error', function () {
|
|
container.textContent = '';
|
|
var fallback = document.createElement('span');
|
|
fallback.className = fallbackClass || 'member-fallback';
|
|
fallback.textContent = initial(name) || 'B';
|
|
container.appendChild(fallback);
|
|
});
|
|
container.appendChild(image);
|
|
}
|
|
|
|
function renderProfile() {
|
|
var image = $('profileAvatar');
|
|
var fallback = $('profileAvatarFallback');
|
|
$('profileName').textContent = mock.profile.name;
|
|
$('profileUID').textContent = 'UID: ' + mock.profile.uid;
|
|
fallback.textContent = initial(mock.profile.name) || 'B';
|
|
image.onload = function () {
|
|
image.hidden = false;
|
|
fallback.hidden = true;
|
|
};
|
|
image.onerror = function () {
|
|
image.hidden = true;
|
|
fallback.hidden = false;
|
|
};
|
|
image.src = mock.profile.avatar;
|
|
}
|
|
|
|
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 memberRow(item) {
|
|
var row = document.createElement('article');
|
|
var avatar = document.createElement('div');
|
|
var main = document.createElement('div');
|
|
var name = document.createElement('div');
|
|
var meta = document.createElement('div');
|
|
var side = document.createElement('div');
|
|
row.className = 'member-row';
|
|
avatar.className = 'member-avatar';
|
|
main.className = 'member-main';
|
|
name.className = 'member-name';
|
|
meta.className = 'member-meta';
|
|
side.className = 'member-side';
|
|
renderImageAvatar(avatar, item.avatar, item.name);
|
|
name.textContent = item.name;
|
|
meta.textContent = 'UID: ' + item.account;
|
|
[
|
|
t('bd_center.team_salary', 'Team salary') +
|
|
': $' +
|
|
money(item.teamSalaryAmount),
|
|
t('bd_center.recharge', 'Recharge') +
|
|
': $' +
|
|
money(item.teamRechargeAmount),
|
|
t('bd_center.host', 'Host') + ': ' + count(item.teamMemberCount),
|
|
].forEach(function (line) {
|
|
var node = document.createElement('div');
|
|
node.textContent = line;
|
|
side.appendChild(node);
|
|
});
|
|
main.appendChild(name);
|
|
main.appendChild(meta);
|
|
row.appendChild(avatar);
|
|
row.appendChild(main);
|
|
row.appendChild(side);
|
|
return row;
|
|
}
|
|
|
|
function renderTeamList() {
|
|
var bill = mock.agencyBill;
|
|
$('billMonthText').textContent = monthLabel(state.billMonth);
|
|
$('billRange').textContent = state.billMonth.replace('-', '.');
|
|
|
|
replaceChildren($('teamListSummary'), [
|
|
summaryCell(
|
|
t('bd_center.agency_number', 'Agency number'),
|
|
count(bill.agencyNumber)
|
|
),
|
|
summaryCell(
|
|
t('bd_center.team_salary', 'Team salary'),
|
|
'$' + money(bill.totalSalary)
|
|
),
|
|
]);
|
|
|
|
var rows = bill.memberBillList.map(memberRow);
|
|
if (!rows.length) {
|
|
var empty = document.createElement('div');
|
|
empty.className = 'empty-state';
|
|
empty.textContent = t('bd_center.no_data', 'No data');
|
|
rows = [empty];
|
|
}
|
|
replaceChildren($('teamListContent'), rows);
|
|
}
|
|
|
|
function monthOption(date) {
|
|
var value = monthValue(date);
|
|
var option = document.createElement('button');
|
|
option.className = 'month-option';
|
|
option.type = 'button';
|
|
option.setAttribute('role', 'option');
|
|
option.setAttribute('data-month', value);
|
|
option.setAttribute('aria-selected', String(value === state.billMonth));
|
|
option.textContent = monthLabel(value);
|
|
if (value === state.billMonth) option.classList.add('is-selected');
|
|
return option;
|
|
}
|
|
|
|
function renderMonthSheet() {
|
|
replaceChildren(
|
|
$('monthOptionList'),
|
|
monthOptions().map(function (date) {
|
|
return monthOption(date);
|
|
})
|
|
);
|
|
}
|
|
|
|
function openMonthSheet() {
|
|
renderMonthSheet();
|
|
$('monthSheet').hidden = false;
|
|
$('monthSheet').setAttribute('aria-hidden', 'false');
|
|
document.body.classList.add('modal-open');
|
|
window.setTimeout(function () {
|
|
var selected = $('monthOptionList').querySelector('.is-selected');
|
|
if (selected && selected.scrollIntoView) {
|
|
selected.scrollIntoView({ block: 'center' });
|
|
}
|
|
}, 0);
|
|
}
|
|
|
|
function closeMonthSheet() {
|
|
$('monthSheet').hidden = true;
|
|
$('monthSheet').setAttribute('aria-hidden', 'true');
|
|
document.body.classList.remove('modal-open');
|
|
}
|
|
|
|
function selectMonth(value) {
|
|
var nextMonth = normalizeBillMonth(value);
|
|
if (!nextMonth || nextMonth === state.billMonth) {
|
|
closeMonthSheet();
|
|
return;
|
|
}
|
|
state.billMonth = nextMonth;
|
|
closeMonthSheet();
|
|
renderTeamList();
|
|
toast(t('bd_center.mock_bill_loaded', 'Mock bill loaded.'));
|
|
}
|
|
|
|
function inviteProfile(item) {
|
|
return item.profile || item;
|
|
}
|
|
|
|
function statusLabel(status) {
|
|
if (status === 'success') return t('bd_center.success', 'Success');
|
|
if (status === 'pending') return t('bd_center.pending', 'Pending');
|
|
return '';
|
|
}
|
|
|
|
function inviteCard(item) {
|
|
var profile = inviteProfile(item);
|
|
var card = document.createElement('article');
|
|
var avatar = document.createElement('div');
|
|
var main = document.createElement('div');
|
|
var name = document.createElement('div');
|
|
var meta = document.createElement('div');
|
|
var side = document.createElement('div');
|
|
card.className = 'invite-user-card';
|
|
avatar.className = 'invite-user-avatar';
|
|
main.className = 'invite-user-main';
|
|
name.className = 'invite-user-name';
|
|
meta.className = 'invite-user-meta';
|
|
side.className = 'invite-user-side';
|
|
renderImageAvatar(
|
|
avatar,
|
|
profile.avatar,
|
|
profile.name,
|
|
'invite-avatar-fallback'
|
|
);
|
|
name.textContent = profile.name;
|
|
meta.textContent = 'UID: ' + profile.account;
|
|
main.appendChild(name);
|
|
main.appendChild(meta);
|
|
|
|
if (item.status === 'pending' || item.status === 'success') {
|
|
var badge = document.createElement('span');
|
|
badge.className = 'invite-badge ' + item.status;
|
|
badge.textContent = statusLabel(item.status);
|
|
side.appendChild(badge);
|
|
if (item.status === 'pending') {
|
|
var cancel = document.createElement('button');
|
|
cancel.className = 'invite-cancel';
|
|
cancel.type = 'button';
|
|
cancel.textContent = t('bd_center.cancel', 'Cancel');
|
|
cancel.addEventListener('click', function () {
|
|
mock.inviteRecords = mock.inviteRecords.filter(
|
|
function (record) {
|
|
return record.id !== item.id;
|
|
}
|
|
);
|
|
toast(
|
|
t(
|
|
'bd_center.application_cancelled',
|
|
'Application cancelled'
|
|
)
|
|
);
|
|
renderInviteModal();
|
|
});
|
|
side.appendChild(cancel);
|
|
}
|
|
} else {
|
|
var invite = document.createElement('button');
|
|
invite.className = 'invite-action';
|
|
invite.type = 'button';
|
|
invite.textContent = t('bd_center.invite', 'Invite');
|
|
invite.addEventListener('click', function () {
|
|
mock.inviteRecords.unshift({
|
|
id: 'inv-' + Date.now(),
|
|
status: 'pending',
|
|
profile: profile,
|
|
});
|
|
state.inviteQuery = '';
|
|
state.inviteMode = 'records';
|
|
state.searched = [];
|
|
$('inviteSearchInput').value = '';
|
|
toast(
|
|
t('bd_center.invitation_submitted', 'Invitation submitted')
|
|
);
|
|
renderInviteModal();
|
|
});
|
|
side.appendChild(invite);
|
|
}
|
|
|
|
card.appendChild(avatar);
|
|
card.appendChild(main);
|
|
card.appendChild(side);
|
|
return card;
|
|
}
|
|
|
|
function setInviteStatus(text, type) {
|
|
var node = $('inviteStatus');
|
|
node.textContent = text || '';
|
|
node.hidden = !text;
|
|
node.classList.toggle('is-error', type === 'error');
|
|
node.classList.toggle('is-success', type === 'success');
|
|
}
|
|
|
|
function renderInviteModal() {
|
|
var rows =
|
|
state.inviteMode === 'search'
|
|
? state.searched.map(inviteCard)
|
|
: mock.inviteRecords.map(inviteCard);
|
|
setInviteStatus(state.inviteStatus, state.inviteStatusType);
|
|
replaceChildren($('inviteList'), rows);
|
|
$('inviteEmpty').hidden =
|
|
rows.length > 0 || Boolean(state.inviteStatus);
|
|
$('inviteSearchButton').disabled = !state.inviteQuery.trim();
|
|
}
|
|
|
|
function openInviteModal() {
|
|
state.inviteQuery = '';
|
|
state.inviteMode = 'records';
|
|
state.searched = [];
|
|
state.inviteStatus = '';
|
|
$('inviteSearchInput').value = '';
|
|
$('inviteModal').hidden = false;
|
|
$('inviteModal').setAttribute('aria-hidden', 'false');
|
|
document.body.classList.add('modal-open');
|
|
renderInviteModal();
|
|
window.setTimeout(function () {
|
|
$('inviteSearchInput').focus();
|
|
}, 80);
|
|
}
|
|
|
|
function closeInviteModal() {
|
|
$('inviteModal').hidden = true;
|
|
$('inviteModal').setAttribute('aria-hidden', 'true');
|
|
document.body.classList.remove('modal-open');
|
|
}
|
|
|
|
function searchInvite(event) {
|
|
event.preventDefault();
|
|
var query = state.inviteQuery.trim().toLowerCase();
|
|
state.inviteMode = 'search';
|
|
state.inviteStatus = '';
|
|
state.inviteStatusType = '';
|
|
state.searched = mock.searchPool.filter(function (profile) {
|
|
return (
|
|
profile.name.toLowerCase().indexOf(query) >= 0 ||
|
|
profile.account.indexOf(query) >= 0 ||
|
|
profile.id.indexOf(query) >= 0
|
|
);
|
|
});
|
|
if (!state.searched.length) {
|
|
state.inviteStatus = t(
|
|
'bd_center.no_users_found',
|
|
'No users found'
|
|
);
|
|
}
|
|
renderInviteModal();
|
|
}
|
|
|
|
function bindEvents() {
|
|
$('backButton').addEventListener('click', function () {
|
|
if (window.HyAppBridge) window.HyAppBridge.back();
|
|
});
|
|
$('withdrawExchangeButton').addEventListener('click', function () {
|
|
toast(
|
|
t(
|
|
'bd_center.mock_navigation',
|
|
'Mock navigation: {label}'
|
|
).replace(
|
|
'{label}',
|
|
t('bd_center.withdraw_exchange', 'Withdraw/Exchange')
|
|
)
|
|
);
|
|
});
|
|
$('inviteAgentButton').addEventListener('click', openInviteModal);
|
|
$('inviteBackdrop').addEventListener('click', closeInviteModal);
|
|
$('inviteCloseButton').addEventListener('click', closeInviteModal);
|
|
$('inviteSearchInput').addEventListener('input', function (event) {
|
|
state.inviteQuery = event.target.value;
|
|
if (!state.inviteQuery.trim()) {
|
|
state.inviteMode = 'records';
|
|
state.searched = [];
|
|
state.inviteStatus = '';
|
|
}
|
|
renderInviteModal();
|
|
});
|
|
$('inviteSearchForm').addEventListener('submit', searchInvite);
|
|
$('billMonthButton').addEventListener('click', openMonthSheet);
|
|
$('monthSheetBackdrop').addEventListener('click', closeMonthSheet);
|
|
$('monthSheetCloseButton').addEventListener('click', closeMonthSheet);
|
|
$('monthOptionList').addEventListener('click', function (event) {
|
|
var option = event.target.closest('[data-month]');
|
|
if (!option) return;
|
|
selectMonth(option.getAttribute('data-month'));
|
|
});
|
|
}
|
|
|
|
function render() {
|
|
renderProfile();
|
|
renderBalance();
|
|
renderTeamList();
|
|
renderInviteModal();
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
render();
|
|
bindEvents();
|
|
if (window.HyAppBridge) {
|
|
window.HyAppBridge.ready({ page: 'bd-center', mock: true });
|
|
}
|
|
});
|
|
|
|
window.addEventListener('hyapp:i18n-ready', function () {
|
|
render();
|
|
});
|
|
})();
|