1197 lines
43 KiB
JavaScript
1197 lines
43 KiB
JavaScript
(function () {
|
|
var ACTION_LABELS = {
|
|
'block-user': 'Block User',
|
|
'send-frame': 'Send Frame',
|
|
'send-car': 'Send Car',
|
|
'send-vip': 'Send VIP',
|
|
'update-user-level': 'Update User Level',
|
|
'add-bd-leader': 'Add BD Leader',
|
|
'add-admin': 'Add Admin',
|
|
'add-superadmin': 'Add Superadmin',
|
|
};
|
|
var DAY_MS = 24 * 60 * 60 * 1000;
|
|
var PERMANENT_BLOCK_MS = 10 * 365 * DAY_MS;
|
|
var RESOURCE_TYPES = {
|
|
'send-frame': 'avatar_frame',
|
|
'send-car': 'vehicle',
|
|
};
|
|
var ACTION_PERMISSION_KEYS = {
|
|
'send-frame': 'canGrantAvatarFrame',
|
|
'send-car': 'canGrantVehicle',
|
|
'update-user-level': 'canUpdateUserLevel',
|
|
'add-bd-leader': 'canAddBdLeader',
|
|
'add-admin': 'canAddAdmin',
|
|
'add-superadmin': 'canAddSuperadmin',
|
|
'block-user': 'canBlockUser',
|
|
};
|
|
var RESOURCE_GRANT_DURATIONS = [
|
|
{ label: '7 days', ms: 7 * DAY_MS },
|
|
{ label: '30 days', ms: 30 * DAY_MS },
|
|
{ label: '90 days', ms: 90 * DAY_MS },
|
|
];
|
|
var DEFAULT_RESOURCE_GRANT_DURATION_MS = RESOURCE_GRANT_DURATIONS[0].ms;
|
|
var TRACK_LABELS = {
|
|
wealth: 'Wealth',
|
|
charm: 'Charm',
|
|
game: 'Game',
|
|
};
|
|
|
|
var api = window.HyAppAPI && window.HyAppAPI.managerCenter;
|
|
var pageState = {
|
|
loading: true,
|
|
denied: false,
|
|
overview: null,
|
|
permissions: {},
|
|
resourcesByType: {},
|
|
};
|
|
var blockState = {
|
|
searchedUser: null,
|
|
selectedDurationMs: 0,
|
|
selectedDurationLabel: '',
|
|
blockedUsers: [],
|
|
submitting: false,
|
|
};
|
|
var levelState = {
|
|
searchedUser: null,
|
|
selectedTrack: 'wealth',
|
|
selectedLevel: 0,
|
|
submitting: false,
|
|
};
|
|
var grantState = {
|
|
action: '',
|
|
resourceType: '',
|
|
title: '',
|
|
searchedUser: null,
|
|
selectedResourceId: '',
|
|
selectedResourceDurationMs: DEFAULT_RESOURCE_GRANT_DURATION_MS,
|
|
selectedVipLevel: 0,
|
|
submitting: false,
|
|
};
|
|
var bdLeaderState = {
|
|
action: '',
|
|
title: '',
|
|
searchedUser: null,
|
|
submitting: false,
|
|
};
|
|
|
|
function $(id) {
|
|
return document.getElementById(id);
|
|
}
|
|
|
|
function root() {
|
|
return document.querySelector('.manager-center');
|
|
}
|
|
|
|
function t(key, fallback) {
|
|
return window.HyAppI18n && window.HyAppI18n.t
|
|
? window.HyAppI18n.t(key, fallback)
|
|
: fallback || key;
|
|
}
|
|
|
|
function toast(message) {
|
|
if (window.HyAppToast && window.HyAppToast.show) {
|
|
window.HyAppToast.show(message);
|
|
return;
|
|
}
|
|
window.alert(message);
|
|
}
|
|
|
|
function showError(error) {
|
|
var message =
|
|
error && error.status === 403
|
|
? 'Permission denied.'
|
|
: (error && error.message) || 'Request failed.';
|
|
toast(message);
|
|
}
|
|
|
|
function commandId(prefix) {
|
|
return [
|
|
prefix || 'manager',
|
|
Date.now(),
|
|
Math.random().toString(16).slice(2, 10),
|
|
].join('-');
|
|
}
|
|
|
|
function userName(user) {
|
|
return (
|
|
user.username ||
|
|
user.name ||
|
|
user.target_username ||
|
|
user.display_user_id ||
|
|
user.target_display_user_id ||
|
|
'User'
|
|
);
|
|
}
|
|
|
|
function userDisplayId(user) {
|
|
return user.display_user_id || user.target_display_user_id || '';
|
|
}
|
|
|
|
function userId(user) {
|
|
return user.user_id || user.target_user_id || '';
|
|
}
|
|
|
|
function avatarUrl(user) {
|
|
return user.avatar || user.target_avatar || '';
|
|
}
|
|
|
|
function initial(text) {
|
|
return String(text || 'M')
|
|
.trim()
|
|
.charAt(0)
|
|
.toUpperCase();
|
|
}
|
|
|
|
function createNode(tag, className, text) {
|
|
var node = document.createElement(tag);
|
|
if (className) node.className = className;
|
|
if (text !== undefined) node.textContent = text;
|
|
return node;
|
|
}
|
|
|
|
function setAvatar(node, user) {
|
|
var url = avatarUrl(user);
|
|
node.innerHTML = '';
|
|
if (url) {
|
|
var img = document.createElement('img');
|
|
img.src = url;
|
|
img.alt = '';
|
|
node.appendChild(img);
|
|
return;
|
|
}
|
|
node.textContent = initial(userName(user) || userDisplayId(user));
|
|
}
|
|
|
|
function formatDateTime(ms) {
|
|
if (!ms) return 'Never';
|
|
var date = new Date(Number(ms));
|
|
var year = date.getFullYear();
|
|
var month = String(date.getMonth() + 1).padStart(2, '0');
|
|
var day = String(date.getDate()).padStart(2, '0');
|
|
var hour = String(date.getHours()).padStart(2, '0');
|
|
var minute = String(date.getMinutes()).padStart(2, '0');
|
|
return year + '-' + month + '-' + day + ' ' + hour + ':' + minute;
|
|
}
|
|
|
|
function setPageLoading(loading) {
|
|
pageState.loading = loading;
|
|
var container = root();
|
|
if (!container) return;
|
|
container.classList.toggle('is-loading', loading);
|
|
}
|
|
|
|
function setDenied(denied) {
|
|
pageState.denied = denied;
|
|
var container = root();
|
|
if (!container) return;
|
|
container.classList.toggle('is-denied', denied);
|
|
}
|
|
|
|
function normalizeUser(item) {
|
|
return {
|
|
user_id: item.user_id || item.userId || item.target_user_id || '',
|
|
display_user_id:
|
|
item.display_user_id ||
|
|
item.displayUserId ||
|
|
item.target_display_user_id ||
|
|
'',
|
|
username: item.username || item.name || item.target_username || '',
|
|
avatar: item.avatar || item.target_avatar || '',
|
|
country: item.country || '',
|
|
levels: item.levels || {},
|
|
};
|
|
}
|
|
|
|
function normalizeBlock(item) {
|
|
return {
|
|
block_id: item.block_id || item.blockId || '',
|
|
target_user_id: item.target_user_id || item.targetUserId || '',
|
|
target_display_user_id:
|
|
item.target_display_user_id || item.targetDisplayUserId || '',
|
|
target_username: item.target_username || item.targetUsername || '',
|
|
target_avatar: item.target_avatar || item.targetAvatar || '',
|
|
country: item.country || '',
|
|
blocked_until_ms:
|
|
item.blocked_until_ms ||
|
|
item.blockedUntilMs ||
|
|
item.unblockAtMs ||
|
|
0,
|
|
status: item.status || 'active',
|
|
};
|
|
}
|
|
|
|
function normalizePermissions(raw) {
|
|
raw = raw || {};
|
|
return {
|
|
canGrantAvatarFrame:
|
|
raw.canGrantAvatarFrame !== undefined
|
|
? Boolean(raw.canGrantAvatarFrame)
|
|
: raw.can_grant_avatar_frame !== undefined
|
|
? Boolean(raw.can_grant_avatar_frame)
|
|
: true,
|
|
canGrantVehicle:
|
|
raw.canGrantVehicle !== undefined
|
|
? Boolean(raw.canGrantVehicle)
|
|
: raw.can_grant_vehicle !== undefined
|
|
? Boolean(raw.can_grant_vehicle)
|
|
: true,
|
|
canUpdateUserLevel:
|
|
raw.canUpdateUserLevel !== undefined
|
|
? Boolean(raw.canUpdateUserLevel)
|
|
: raw.can_update_user_level !== undefined
|
|
? Boolean(raw.can_update_user_level)
|
|
: true,
|
|
canAddBdLeader:
|
|
raw.canAddBdLeader !== undefined
|
|
? Boolean(raw.canAddBdLeader)
|
|
: raw.can_add_bd_leader !== undefined
|
|
? Boolean(raw.can_add_bd_leader)
|
|
: true,
|
|
canAddAdmin:
|
|
raw.canAddAdmin !== undefined
|
|
? Boolean(raw.canAddAdmin)
|
|
: raw.can_add_admin !== undefined
|
|
? Boolean(raw.can_add_admin)
|
|
: true,
|
|
canAddSuperadmin:
|
|
raw.canAddSuperadmin !== undefined
|
|
? Boolean(raw.canAddSuperadmin)
|
|
: raw.can_add_superadmin !== undefined
|
|
? Boolean(raw.can_add_superadmin)
|
|
: true,
|
|
canBlockUser:
|
|
raw.canBlockUser !== undefined
|
|
? Boolean(raw.canBlockUser)
|
|
: raw.can_block_user !== undefined
|
|
? Boolean(raw.can_block_user)
|
|
: true,
|
|
};
|
|
}
|
|
|
|
function canUseAction(action) {
|
|
var key = ACTION_PERMISSION_KEYS[action];
|
|
if (!key) return true;
|
|
return pageState.permissions[key] !== false;
|
|
}
|
|
|
|
function renderActionPermissions() {
|
|
document.querySelectorAll('[data-action]').forEach(function (button) {
|
|
var action = button.getAttribute('data-action');
|
|
button.hidden = !canUseAction(action);
|
|
});
|
|
}
|
|
|
|
function ensureActionAllowed(action) {
|
|
if (canUseAction(action)) return true;
|
|
toast('Permission denied.');
|
|
return false;
|
|
}
|
|
|
|
function apiSearchUser(keyword) {
|
|
if (!api || !api.searchUsers) {
|
|
return Promise.reject(new Error('API not available.'));
|
|
}
|
|
return api.searchUsers(keyword, 20).then(function (data) {
|
|
var items = (data && data.items) || [];
|
|
return items.map(normalizeUser);
|
|
});
|
|
}
|
|
|
|
function loadOverview() {
|
|
if (!api || !api.overview) {
|
|
setDenied(true);
|
|
toast('Permission denied.');
|
|
return Promise.resolve();
|
|
}
|
|
setPageLoading(true);
|
|
return api
|
|
.overview()
|
|
.then(function (data) {
|
|
pageState.overview = data || {};
|
|
pageState.permissions = normalizePermissions(
|
|
pageState.overview.permissions || pageState.overview
|
|
);
|
|
renderActionPermissions();
|
|
setDenied(false);
|
|
return canUseAction('block-user') ? refreshBlocks() : Promise.resolve();
|
|
})
|
|
.catch(function (error) {
|
|
setDenied(true);
|
|
showError(error);
|
|
})
|
|
.finally(function () {
|
|
setPageLoading(false);
|
|
});
|
|
}
|
|
|
|
function refreshBlocks() {
|
|
if (!api || !api.listBlocks) return Promise.resolve();
|
|
return api
|
|
.listBlocks('active', 50)
|
|
.then(function (data) {
|
|
blockState.blockedUsers = ((data && data.items) || []).map(
|
|
normalizeBlock
|
|
);
|
|
renderBlockedList();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function goBack() {
|
|
if (window.HyAppBridge && window.HyAppBridge.back) {
|
|
window.HyAppBridge.back();
|
|
return;
|
|
}
|
|
if (window.history.length > 1) {
|
|
window.history.back();
|
|
}
|
|
}
|
|
|
|
function renderUserRow(user, actionText, onAction) {
|
|
var row = createNode('div', 'searched-user');
|
|
var avatar = createNode('div', 'searched-avatar');
|
|
setAvatar(avatar, user);
|
|
var copy = createNode('div', 'blocked-row-main');
|
|
copy.appendChild(createNode('div', 'searched-name', userName(user)));
|
|
copy.appendChild(
|
|
createNode('div', 'searched-meta', 'UID: ' + userDisplayId(user))
|
|
);
|
|
var button = createNode('button', 'user-block-button', actionText);
|
|
button.type = 'button';
|
|
button.addEventListener('click', onAction);
|
|
row.appendChild(avatar);
|
|
row.appendChild(copy);
|
|
row.appendChild(button);
|
|
return row;
|
|
}
|
|
|
|
function renderSearchResult() {
|
|
var container = $('blockSearchResult');
|
|
if (!container) return;
|
|
container.innerHTML = '';
|
|
if (!blockState.searchedUser) return;
|
|
container.appendChild(
|
|
renderUserRow(blockState.searchedUser, 'Block', function () {
|
|
selectDuration('86400000', '1 day');
|
|
var panel = $('blockDurationPanel');
|
|
if (panel) panel.hidden = false;
|
|
})
|
|
);
|
|
}
|
|
|
|
function setupLevelGrid() {
|
|
var grid = $('levelGrid');
|
|
if (!grid || grid.children.length) return;
|
|
for (var level = 1; level <= 11; level += 1) {
|
|
var button = createNode('button', '', 'Lv. ' + level);
|
|
button.type = 'button';
|
|
button.setAttribute('data-level', String(level));
|
|
grid.appendChild(button);
|
|
}
|
|
}
|
|
|
|
function ensureTrackSelector() {
|
|
var panel = $('levelPanel');
|
|
if (!panel || $('levelTrackGrid')) return;
|
|
var title = createNode(
|
|
'div',
|
|
'block-section-title',
|
|
'Select level type'
|
|
);
|
|
var grid = createNode('div', 'track-grid');
|
|
grid.id = 'levelTrackGrid';
|
|
Object.keys(TRACK_LABELS).forEach(function (track) {
|
|
var button = createNode('button', '', TRACK_LABELS[track]);
|
|
button.type = 'button';
|
|
button.setAttribute('data-track', track);
|
|
button.addEventListener('click', function () {
|
|
levelState.selectedTrack = track;
|
|
renderLevelButtons();
|
|
renderLevelSearchResult();
|
|
});
|
|
grid.appendChild(button);
|
|
});
|
|
panel.insertBefore(grid, panel.firstChild);
|
|
panel.insertBefore(title, grid);
|
|
}
|
|
|
|
function currentTrackLevel(user) {
|
|
var track = levelState.selectedTrack;
|
|
var levels = (user && user.levels) || {};
|
|
return levels[track] && levels[track].level ? levels[track].level : 0;
|
|
}
|
|
|
|
function renderLevelSearchResult() {
|
|
var container = $('levelSearchResult');
|
|
if (!container) return;
|
|
container.innerHTML = '';
|
|
if (!levelState.searchedUser) return;
|
|
var user = levelState.searchedUser;
|
|
var row = createNode('div', 'searched-user');
|
|
var avatar = createNode('div', 'searched-avatar');
|
|
setAvatar(avatar, user);
|
|
var copy = createNode('div', 'blocked-row-main');
|
|
copy.appendChild(createNode('div', 'searched-name', userName(user)));
|
|
copy.appendChild(
|
|
createNode(
|
|
'div',
|
|
'searched-meta',
|
|
'UID: ' +
|
|
userDisplayId(user) +
|
|
' · ' +
|
|
TRACK_LABELS[levelState.selectedTrack] +
|
|
' Lv. ' +
|
|
currentTrackLevel(user)
|
|
)
|
|
);
|
|
var badge = createNode(
|
|
'div',
|
|
'block-list-count',
|
|
'Lv. ' + currentTrackLevel(user)
|
|
);
|
|
row.appendChild(avatar);
|
|
row.appendChild(copy);
|
|
row.appendChild(badge);
|
|
container.appendChild(row);
|
|
}
|
|
|
|
function renderLevelButtons() {
|
|
var trackButtons = document.querySelectorAll(
|
|
'#levelTrackGrid [data-track]'
|
|
);
|
|
trackButtons.forEach(function (button) {
|
|
button.classList.toggle(
|
|
'is-selected',
|
|
button.getAttribute('data-track') === levelState.selectedTrack
|
|
);
|
|
});
|
|
var buttons = document.querySelectorAll('#levelGrid [data-level]');
|
|
buttons.forEach(function (button) {
|
|
var isSelected =
|
|
Number(button.getAttribute('data-level')) ===
|
|
levelState.selectedLevel;
|
|
button.classList.toggle('is-selected', isSelected);
|
|
});
|
|
var confirmButton = $('confirmLevelButton');
|
|
if (confirmButton) {
|
|
confirmButton.disabled =
|
|
!levelState.searchedUser ||
|
|
!levelState.selectedLevel ||
|
|
levelState.submitting;
|
|
}
|
|
}
|
|
|
|
function selectLevel(level) {
|
|
levelState.selectedLevel = Number(level) || 0;
|
|
renderLevelButtons();
|
|
}
|
|
|
|
function renderDurationButtons() {
|
|
var buttons = document.querySelectorAll(
|
|
'#durationGrid [data-duration-ms]'
|
|
);
|
|
buttons.forEach(function (button) {
|
|
var isSelected =
|
|
Number(button.getAttribute('data-duration-ms')) ===
|
|
blockState.selectedDurationMs;
|
|
button.classList.toggle('is-selected', isSelected);
|
|
});
|
|
var confirmButton = $('confirmBlockButton');
|
|
if (confirmButton) {
|
|
confirmButton.disabled =
|
|
!blockState.searchedUser || blockState.submitting;
|
|
}
|
|
}
|
|
|
|
function selectDuration(durationMs, label) {
|
|
blockState.selectedDurationMs = Number(durationMs) || 0;
|
|
blockState.selectedDurationLabel = label || 'Permanent';
|
|
renderDurationButtons();
|
|
}
|
|
|
|
function renderBlockedList() {
|
|
var container = $('blockedList');
|
|
var count = $('blockListCount');
|
|
if (!container) return;
|
|
container.innerHTML = '';
|
|
if (count) count.textContent = String(blockState.blockedUsers.length);
|
|
if (!blockState.blockedUsers.length) {
|
|
container.appendChild(
|
|
createNode('div', 'blocked-empty', 'No blocked users.')
|
|
);
|
|
return;
|
|
}
|
|
blockState.blockedUsers.forEach(function (user) {
|
|
var row = createNode('div', 'blocked-row');
|
|
var avatar = createNode('div', 'blocked-avatar');
|
|
setAvatar(avatar, user);
|
|
var copy = createNode('div', 'blocked-row-main');
|
|
copy.appendChild(createNode('div', 'blocked-name', userName(user)));
|
|
copy.appendChild(
|
|
createNode('div', 'blocked-meta', 'UID: ' + userDisplayId(user))
|
|
);
|
|
copy.appendChild(
|
|
createNode(
|
|
'div',
|
|
'blocked-time',
|
|
'Unblock time: ' + formatDateTime(user.blocked_until_ms)
|
|
)
|
|
);
|
|
var unblockButton = createNode(
|
|
'button',
|
|
'user-unblock-button',
|
|
'Unblock'
|
|
);
|
|
unblockButton.type = 'button';
|
|
unblockButton.addEventListener('click', function () {
|
|
unblockUser(user.block_id);
|
|
});
|
|
row.appendChild(avatar);
|
|
row.appendChild(copy);
|
|
row.appendChild(unblockButton);
|
|
container.appendChild(row);
|
|
});
|
|
}
|
|
|
|
function unblockUser(blockId) {
|
|
if (!blockId || blockState.submitting) return;
|
|
blockState.submitting = true;
|
|
api.unblockUser(blockId, { reason: 'manager_center' })
|
|
.then(function () {
|
|
toast('User unblocked.');
|
|
return refreshBlocks();
|
|
})
|
|
.catch(showError)
|
|
.finally(function () {
|
|
blockState.submitting = false;
|
|
});
|
|
}
|
|
|
|
function resetBlockSearch() {
|
|
var input = $('blockSearchInput');
|
|
var panel = $('blockDurationPanel');
|
|
blockState.searchedUser = null;
|
|
blockState.selectedDurationMs = 0;
|
|
blockState.selectedDurationLabel = '';
|
|
if (input) input.value = '';
|
|
if (panel) panel.hidden = true;
|
|
renderSearchResult();
|
|
renderDurationButtons();
|
|
}
|
|
|
|
function openBlockModal() {
|
|
if (pageState.denied) {
|
|
toast('Permission denied.');
|
|
return;
|
|
}
|
|
if (!ensureActionAllowed('block-user')) return;
|
|
var modal = $('blockModal');
|
|
if (!modal) return;
|
|
resetBlockSearch();
|
|
refreshBlocks();
|
|
modal.hidden = false;
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
setTimeout(function () {
|
|
var input = $('blockSearchInput');
|
|
if (input) input.focus();
|
|
}, 30);
|
|
}
|
|
|
|
function closeBlockModal() {
|
|
var modal = $('blockModal');
|
|
if (!modal) return;
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function resetLevelSearch() {
|
|
var input = $('levelSearchInput');
|
|
var panel = $('levelPanel');
|
|
levelState.searchedUser = null;
|
|
levelState.selectedTrack = 'wealth';
|
|
levelState.selectedLevel = 0;
|
|
if (input) input.value = '';
|
|
if (panel) panel.hidden = true;
|
|
renderLevelSearchResult();
|
|
renderLevelButtons();
|
|
}
|
|
|
|
function openLevelModal() {
|
|
if (pageState.denied) {
|
|
toast('Permission denied.');
|
|
return;
|
|
}
|
|
if (!ensureActionAllowed('update-user-level')) return;
|
|
var modal = $('levelModal');
|
|
if (!modal) return;
|
|
resetLevelSearch();
|
|
modal.hidden = false;
|
|
modal.setAttribute('aria-hidden', 'false');
|
|
setTimeout(function () {
|
|
var input = $('levelSearchInput');
|
|
if (input) input.focus();
|
|
}, 30);
|
|
}
|
|
|
|
function closeLevelModal() {
|
|
var modal = $('levelModal');
|
|
if (!modal) return;
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function searchBlockUser() {
|
|
var input = $('blockSearchInput');
|
|
var value = input ? input.value.trim() : '';
|
|
var panel = $('blockDurationPanel');
|
|
if (!value) {
|
|
toast('Enter user ID.');
|
|
return;
|
|
}
|
|
apiSearchUser(value)
|
|
.then(function (items) {
|
|
if (!items.length) {
|
|
blockState.searchedUser = null;
|
|
toast('No user found.');
|
|
return;
|
|
}
|
|
blockState.searchedUser = items[0];
|
|
blockState.selectedDurationMs = 0;
|
|
blockState.selectedDurationLabel = '';
|
|
if (panel) panel.hidden = true;
|
|
renderSearchResult();
|
|
renderDurationButtons();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function confirmBlockUser() {
|
|
if (!blockState.searchedUser) {
|
|
toast('Search a user first.');
|
|
return;
|
|
}
|
|
if (blockState.submitting) return;
|
|
var durationMs = blockState.selectedDurationMs || PERMANENT_BLOCK_MS;
|
|
blockState.submitting = true;
|
|
renderDurationButtons();
|
|
api.blockUser({
|
|
command_id: commandId('manager-block'),
|
|
target_user_id: userId(blockState.searchedUser),
|
|
blocked_until_ms: Date.now() + durationMs,
|
|
reason: 'manager_center',
|
|
})
|
|
.then(function () {
|
|
toast('User blocked.');
|
|
resetBlockSearch();
|
|
return refreshBlocks();
|
|
})
|
|
.catch(showError)
|
|
.finally(function () {
|
|
blockState.submitting = false;
|
|
renderDurationButtons();
|
|
});
|
|
}
|
|
|
|
function searchLevelUser() {
|
|
var input = $('levelSearchInput');
|
|
var value = input ? input.value.trim() : '';
|
|
var panel = $('levelPanel');
|
|
if (!value) {
|
|
toast('Enter user ID.');
|
|
return;
|
|
}
|
|
apiSearchUser(value)
|
|
.then(function (items) {
|
|
if (!items.length) {
|
|
levelState.searchedUser = null;
|
|
toast('No user found.');
|
|
return;
|
|
}
|
|
levelState.searchedUser = items[0];
|
|
levelState.selectedLevel = 0;
|
|
if (panel) panel.hidden = false;
|
|
renderLevelSearchResult();
|
|
renderLevelButtons();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function confirmLevelUpdate() {
|
|
if (!levelState.searchedUser) {
|
|
toast('Search a user first.');
|
|
return;
|
|
}
|
|
if (!levelState.selectedLevel) {
|
|
toast('Select a user level.');
|
|
return;
|
|
}
|
|
if (levelState.submitting) return;
|
|
levelState.submitting = true;
|
|
renderLevelButtons();
|
|
api.updateLevel({
|
|
command_id: commandId('manager-level'),
|
|
target_user_id: userId(levelState.searchedUser),
|
|
track: levelState.selectedTrack,
|
|
level: levelState.selectedLevel,
|
|
reason: 'manager_center',
|
|
})
|
|
.then(function () {
|
|
toast('User level updated.');
|
|
closeLevelModal();
|
|
})
|
|
.catch(showError)
|
|
.finally(function () {
|
|
levelState.submitting = false;
|
|
renderLevelButtons();
|
|
});
|
|
}
|
|
|
|
function ensureGrantModal() {
|
|
if ($('grantModal')) return;
|
|
var modal = createNode('div', 'block-modal grant-modal');
|
|
modal.id = 'grantModal';
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
modal.innerHTML =
|
|
'<button class="block-backdrop" id="grantBackdrop" type="button" aria-label="Close"></button>' +
|
|
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="grantDialogTitle">' +
|
|
'<div class="block-dialog-header"><h2 id="grantDialogTitle">Send</h2>' +
|
|
'<button class="block-close" id="grantCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
|
|
'<div class="block-dialog-body"><div class="block-field"><label for="grantSearchInput">User ID</label>' +
|
|
'<div class="block-search-row"><input id="grantSearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="grantSearchButton" type="button">Search</button></div></div>' +
|
|
'<div class="block-search-result" id="grantSearchResult" aria-live="polite"></div>' +
|
|
'<div class="level-panel" id="grantPanel" hidden><div class="block-section-title" id="grantOptionTitle">Select item</div><div class="grant-option-grid" id="grantOptionGrid"></div><div class="block-section-title" id="grantDurationTitle" hidden>Select duration</div><div class="duration-grid grant-duration-grid" id="grantDurationGrid" hidden></div><button class="confirm-level-button" id="confirmGrantButton" type="button" disabled>Send now</button></div></div></section>';
|
|
document.querySelector('.manager-center').appendChild(modal);
|
|
$('grantBackdrop').addEventListener('click', closeGrantModal);
|
|
$('grantCloseButton').addEventListener('click', closeGrantModal);
|
|
$('grantSearchButton').addEventListener('click', searchGrantUser);
|
|
$('grantSearchInput').addEventListener('keydown', function (event) {
|
|
if (event.key === 'Enter') searchGrantUser();
|
|
});
|
|
$('confirmGrantButton').addEventListener('click', confirmGrant);
|
|
}
|
|
|
|
function openGrantModal(action) {
|
|
if (pageState.denied) {
|
|
toast('Permission denied.');
|
|
return;
|
|
}
|
|
if (!ensureActionAllowed(action)) return;
|
|
ensureGrantModal();
|
|
grantState.action = action;
|
|
grantState.resourceType = RESOURCE_TYPES[action] || '';
|
|
grantState.title = ACTION_LABELS[action] || 'Send';
|
|
grantState.searchedUser = null;
|
|
grantState.selectedResourceId = '';
|
|
grantState.selectedResourceDurationMs =
|
|
DEFAULT_RESOURCE_GRANT_DURATION_MS;
|
|
grantState.selectedVipLevel = 0;
|
|
$('grantDialogTitle').textContent = grantState.title;
|
|
$('grantSearchInput').value = '';
|
|
$('grantSearchResult').innerHTML = '';
|
|
$('grantPanel').hidden = true;
|
|
$('grantModal').hidden = false;
|
|
$('grantModal').setAttribute('aria-hidden', 'false');
|
|
renderGrantOptions();
|
|
if (grantState.resourceType)
|
|
loadGrantResources(grantState.resourceType);
|
|
setTimeout(function () {
|
|
$('grantSearchInput').focus();
|
|
}, 30);
|
|
}
|
|
|
|
function closeGrantModal() {
|
|
var modal = $('grantModal');
|
|
if (!modal) return;
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function loadGrantResources(resourceType) {
|
|
if (pageState.resourcesByType[resourceType]) {
|
|
renderGrantOptions();
|
|
return;
|
|
}
|
|
api.listResources(resourceType, 20)
|
|
.then(function (data) {
|
|
pageState.resourcesByType[resourceType] =
|
|
(data && data.items) || [];
|
|
renderGrantOptions();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function searchGrantUser() {
|
|
var value = $('grantSearchInput').value.trim();
|
|
if (!value) {
|
|
toast('Enter user ID.');
|
|
return;
|
|
}
|
|
apiSearchUser(value)
|
|
.then(function (items) {
|
|
if (!items.length) {
|
|
grantState.searchedUser = null;
|
|
toast('No user found.');
|
|
return;
|
|
}
|
|
grantState.searchedUser = items[0];
|
|
var container = $('grantSearchResult');
|
|
container.innerHTML = '';
|
|
container.appendChild(
|
|
renderUserRow(
|
|
grantState.searchedUser,
|
|
'Select',
|
|
function () {
|
|
$('grantPanel').hidden = false;
|
|
renderGrantOptions();
|
|
}
|
|
)
|
|
);
|
|
$('grantPanel').hidden = false;
|
|
renderGrantOptions();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function renderGrantOptions() {
|
|
var grid = $('grantOptionGrid');
|
|
if (!grid) return;
|
|
grid.innerHTML = '';
|
|
var durationTitle = $('grantDurationTitle');
|
|
var durationGrid = $('grantDurationGrid');
|
|
if (durationTitle) {
|
|
durationTitle.hidden = false;
|
|
durationTitle.textContent =
|
|
grantState.action === 'send-vip'
|
|
? 'Duration: 30 days'
|
|
: 'Select duration';
|
|
}
|
|
if (durationGrid) {
|
|
durationGrid.innerHTML = '';
|
|
durationGrid.hidden = grantState.action === 'send-vip';
|
|
}
|
|
if (grantState.action === 'send-vip') {
|
|
$('grantOptionTitle').textContent = 'Select VIP level';
|
|
[1, 2, 3].forEach(function (level) {
|
|
var button = createNode('button', '', 'VIP ' + level);
|
|
button.type = 'button';
|
|
button.classList.toggle(
|
|
'is-selected',
|
|
grantState.selectedVipLevel === level
|
|
);
|
|
button.addEventListener('click', function () {
|
|
grantState.selectedVipLevel = level;
|
|
renderGrantOptions();
|
|
});
|
|
grid.appendChild(button);
|
|
});
|
|
} else {
|
|
$('grantOptionTitle').textContent = 'Select resource';
|
|
var resources =
|
|
pageState.resourcesByType[grantState.resourceType] || [];
|
|
if (!resources.length) {
|
|
grid.appendChild(
|
|
createNode('div', 'blocked-empty', 'No resource available.')
|
|
);
|
|
}
|
|
resources.forEach(function (resource) {
|
|
var button = createNode('button', 'grant-resource-option');
|
|
button.type = 'button';
|
|
button.classList.toggle(
|
|
'is-selected',
|
|
String(grantState.selectedResourceId) ===
|
|
String(resource.resource_id)
|
|
);
|
|
button.textContent =
|
|
resource.name || 'Resource #' + resource.resource_id;
|
|
button.addEventListener('click', function () {
|
|
grantState.selectedResourceId = resource.resource_id;
|
|
renderGrantOptions();
|
|
});
|
|
grid.appendChild(button);
|
|
});
|
|
if (durationGrid) {
|
|
RESOURCE_GRANT_DURATIONS.forEach(function (item) {
|
|
var durationButton = createNode('button', '', item.label);
|
|
durationButton.type = 'button';
|
|
durationButton.classList.toggle(
|
|
'is-selected',
|
|
grantState.selectedResourceDurationMs === item.ms
|
|
);
|
|
durationButton.addEventListener('click', function () {
|
|
grantState.selectedResourceDurationMs = item.ms;
|
|
renderGrantOptions();
|
|
});
|
|
durationGrid.appendChild(durationButton);
|
|
});
|
|
}
|
|
}
|
|
var confirm = $('confirmGrantButton');
|
|
if (confirm) {
|
|
confirm.disabled =
|
|
!grantState.searchedUser ||
|
|
grantState.submitting ||
|
|
(grantState.action === 'send-vip'
|
|
? !grantState.selectedVipLevel
|
|
: !grantState.selectedResourceId ||
|
|
!grantState.selectedResourceDurationMs);
|
|
}
|
|
}
|
|
|
|
function confirmGrant() {
|
|
if (!grantState.searchedUser || grantState.submitting) return;
|
|
grantState.submitting = true;
|
|
renderGrantOptions();
|
|
var payload = {
|
|
command_id: commandId('manager-grant'),
|
|
target_user_id: userId(grantState.searchedUser),
|
|
reason: 'manager_center',
|
|
};
|
|
var request;
|
|
if (grantState.action === 'send-vip') {
|
|
payload.level = grantState.selectedVipLevel;
|
|
request = api.grantVip(payload);
|
|
} else {
|
|
payload.resource_id = grantState.selectedResourceId;
|
|
payload.duration_ms = grantState.selectedResourceDurationMs;
|
|
request = api.grantResource(payload);
|
|
}
|
|
request
|
|
.then(function () {
|
|
toast('Sent successfully.');
|
|
closeGrantModal();
|
|
})
|
|
.catch(showError)
|
|
.finally(function () {
|
|
grantState.submitting = false;
|
|
renderGrantOptions();
|
|
});
|
|
}
|
|
|
|
function ensureBDLeaderModal() {
|
|
if ($('bdLeaderModal')) return;
|
|
var modal = createNode('div', 'block-modal bd-leader-modal');
|
|
modal.id = 'bdLeaderModal';
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
modal.innerHTML =
|
|
'<button class="block-backdrop" id="bdLeaderBackdrop" type="button" aria-label="Close"></button>' +
|
|
'<section class="block-dialog" role="dialog" aria-modal="true" aria-labelledby="bdLeaderDialogTitle">' +
|
|
'<div class="block-dialog-header"><h2 id="bdLeaderDialogTitle">Add BD Leader</h2>' +
|
|
'<button class="block-close" id="bdLeaderCloseButton" type="button" aria-label="Close"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 6l12 12M18 6 6 18"/></svg></button></div>' +
|
|
'<div class="block-dialog-body"><div class="block-field"><label for="bdLeaderSearchInput">User ID</label>' +
|
|
'<div class="block-search-row"><input id="bdLeaderSearchInput" type="text" inputmode="numeric" autocomplete="off" placeholder="Enter user ID"/><button id="bdLeaderSearchButton" type="button">Search</button></div></div>' +
|
|
'<div class="block-search-result" id="bdLeaderSearchResult" aria-live="polite"></div>' +
|
|
'<div class="level-panel" id="bdLeaderPanel" hidden><div class="block-section-title">Add this user as BD Leader</div>' +
|
|
'<button class="confirm-level-button" id="confirmBDLeaderButton" type="button" disabled>Add BD Leader</button></div></div></section>';
|
|
document.querySelector('.manager-center').appendChild(modal);
|
|
$('bdLeaderBackdrop').addEventListener('click', closeBDLeaderModal);
|
|
$('bdLeaderCloseButton').addEventListener('click', closeBDLeaderModal);
|
|
$('bdLeaderSearchButton').addEventListener('click', searchBDLeaderUser);
|
|
$('bdLeaderSearchInput').addEventListener('keydown', function (event) {
|
|
if (event.key === 'Enter') searchBDLeaderUser();
|
|
});
|
|
$('confirmBDLeaderButton').addEventListener('click', confirmBDLeader);
|
|
}
|
|
|
|
function openBDLeaderModal(action) {
|
|
if (pageState.denied) {
|
|
toast('Permission denied.');
|
|
return;
|
|
}
|
|
if (!ensureActionAllowed(action)) return;
|
|
ensureBDLeaderModal();
|
|
bdLeaderState.action = action;
|
|
bdLeaderState.title = ACTION_LABELS[action] || 'Add BD Leader';
|
|
bdLeaderState.searchedUser = null;
|
|
bdLeaderState.submitting = false;
|
|
$('bdLeaderDialogTitle').textContent = bdLeaderState.title;
|
|
$('confirmBDLeaderButton').textContent = bdLeaderState.title;
|
|
$('bdLeaderSearchInput').value = '';
|
|
$('bdLeaderSearchResult').innerHTML = '';
|
|
$('bdLeaderPanel').hidden = true;
|
|
renderBDLeaderActions();
|
|
$('bdLeaderModal').hidden = false;
|
|
$('bdLeaderModal').setAttribute('aria-hidden', 'false');
|
|
setTimeout(function () {
|
|
$('bdLeaderSearchInput').focus();
|
|
}, 30);
|
|
}
|
|
|
|
function closeBDLeaderModal() {
|
|
var modal = $('bdLeaderModal');
|
|
if (!modal) return;
|
|
modal.hidden = true;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
}
|
|
|
|
function searchBDLeaderUser() {
|
|
var value = $('bdLeaderSearchInput').value.trim();
|
|
if (!value) {
|
|
toast('Enter user ID.');
|
|
return;
|
|
}
|
|
apiSearchUser(value)
|
|
.then(function (items) {
|
|
if (!items.length) {
|
|
bdLeaderState.searchedUser = null;
|
|
$('bdLeaderSearchResult').innerHTML = '';
|
|
$('bdLeaderPanel').hidden = true;
|
|
renderBDLeaderActions();
|
|
toast('No user found.');
|
|
return;
|
|
}
|
|
bdLeaderState.searchedUser = items[0];
|
|
var container = $('bdLeaderSearchResult');
|
|
container.innerHTML = '';
|
|
container.appendChild(
|
|
renderUserRow(
|
|
bdLeaderState.searchedUser,
|
|
'Select',
|
|
function () {
|
|
$('bdLeaderPanel').hidden = false;
|
|
renderBDLeaderActions();
|
|
}
|
|
)
|
|
);
|
|
$('bdLeaderPanel').hidden = false;
|
|
renderBDLeaderActions();
|
|
})
|
|
.catch(showError);
|
|
}
|
|
|
|
function renderBDLeaderActions() {
|
|
var confirm = $('confirmBDLeaderButton');
|
|
if (!confirm) return;
|
|
confirm.disabled =
|
|
!bdLeaderState.searchedUser || bdLeaderState.submitting;
|
|
}
|
|
|
|
function confirmBDLeader() {
|
|
if (!bdLeaderState.searchedUser) {
|
|
toast('Search a user first.');
|
|
return;
|
|
}
|
|
if (!api || !api.createBDLeader) {
|
|
toast('API not available.');
|
|
return;
|
|
}
|
|
if (bdLeaderState.submitting) return;
|
|
bdLeaderState.submitting = true;
|
|
renderBDLeaderActions();
|
|
api.createBDLeader({
|
|
command_id: commandId('manager-bd-leader'),
|
|
target_user_id: userId(bdLeaderState.searchedUser),
|
|
reason: 'manager_center',
|
|
})
|
|
.then(function () {
|
|
toast('BD Leader added.');
|
|
closeBDLeaderModal();
|
|
})
|
|
.catch(showError)
|
|
.finally(function () {
|
|
bdLeaderState.submitting = false;
|
|
renderBDLeaderActions();
|
|
});
|
|
}
|
|
|
|
function bindEvents() {
|
|
var backButton = $('backButton');
|
|
if (backButton) backButton.addEventListener('click', goBack);
|
|
document.querySelectorAll('[data-action]').forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
var action = button.getAttribute('data-action');
|
|
if (!ensureActionAllowed(action)) {
|
|
return;
|
|
}
|
|
if (action === 'block-user') {
|
|
openBlockModal();
|
|
return;
|
|
}
|
|
if (action === 'update-user-level') {
|
|
openLevelModal();
|
|
return;
|
|
}
|
|
if (
|
|
action === 'add-bd-leader' ||
|
|
action === 'add-admin' ||
|
|
action === 'add-superadmin'
|
|
) {
|
|
openBDLeaderModal(action);
|
|
return;
|
|
}
|
|
openGrantModal(action);
|
|
});
|
|
});
|
|
setupLevelGrid();
|
|
ensureTrackSelector();
|
|
var closeButton = $('blockCloseButton');
|
|
var backdrop = $('blockBackdrop');
|
|
var searchButton = $('blockSearchButton');
|
|
var searchInput = $('blockSearchInput');
|
|
var confirmButton = $('confirmBlockButton');
|
|
var levelCloseButton = $('levelCloseButton');
|
|
var levelBackdrop = $('levelBackdrop');
|
|
var levelSearchButton = $('levelSearchButton');
|
|
var levelSearchInput = $('levelSearchInput');
|
|
var confirmLevelButton = $('confirmLevelButton');
|
|
if (closeButton) closeButton.addEventListener('click', closeBlockModal);
|
|
if (backdrop) backdrop.addEventListener('click', closeBlockModal);
|
|
if (levelCloseButton)
|
|
levelCloseButton.addEventListener('click', closeLevelModal);
|
|
if (levelBackdrop)
|
|
levelBackdrop.addEventListener('click', closeLevelModal);
|
|
if (searchButton)
|
|
searchButton.addEventListener('click', searchBlockUser);
|
|
if (searchInput) {
|
|
searchInput.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Enter') searchBlockUser();
|
|
});
|
|
}
|
|
if (levelSearchButton)
|
|
levelSearchButton.addEventListener('click', searchLevelUser);
|
|
if (levelSearchInput) {
|
|
levelSearchInput.addEventListener('keydown', function (event) {
|
|
if (event.key === 'Enter') searchLevelUser();
|
|
});
|
|
}
|
|
if (confirmButton)
|
|
confirmButton.addEventListener('click', confirmBlockUser);
|
|
if (confirmLevelButton) {
|
|
confirmLevelButton.addEventListener('click', confirmLevelUpdate);
|
|
}
|
|
document
|
|
.querySelectorAll('#durationGrid [data-duration-ms]')
|
|
.forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
selectDuration(
|
|
button.getAttribute('data-duration-ms'),
|
|
button.getAttribute('data-duration-label')
|
|
);
|
|
});
|
|
});
|
|
document
|
|
.querySelectorAll('#levelGrid [data-level]')
|
|
.forEach(function (button) {
|
|
button.addEventListener('click', function () {
|
|
selectLevel(button.getAttribute('data-level'));
|
|
});
|
|
});
|
|
}
|
|
|
|
function init() {
|
|
bindEvents();
|
|
renderBlockedList();
|
|
loadOverview();
|
|
if (window.HyAppBridge && window.HyAppBridge.ready) {
|
|
window.HyAppBridge.ready({ page: 'manager-center' });
|
|
}
|
|
}
|
|
|
|
if (document.readyState === 'loading') {
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
} else {
|
|
init();
|
|
}
|
|
})();
|