feat: searchable sticky user sidebar with scroll for wallets page
- Added search field at top of sidebar filtering by username or telegram ID - Sidebar is sticky (stays in view while scrolling right panel) - Sidebar max-height matches viewport for natural scroll with hundreds of users - Each user item has data-name/data-tgid/data-id for instant JS filtering - Stats section scrolls naturally below user wallets
This commit is contained in:
@@ -352,19 +352,47 @@ pre { font-size: 0.8rem; white-space: pre-wrap; word-break: break-all; max-width
|
||||
background: var(--card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1rem;
|
||||
max-height: 70vh;
|
||||
overflow-y: auto;
|
||||
padding: 0;
|
||||
position: sticky;
|
||||
top: 1rem;
|
||||
max-height: calc(100vh - 2rem);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.wallet-sidebar h3 {
|
||||
margin-bottom: 0.75rem;
|
||||
.wallet-sidebar-header {
|
||||
padding: 0.75rem 1rem 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wallet-sidebar-header h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.wallet-search {
|
||||
width: 100%;
|
||||
padding: 0.4rem 0.6rem;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.wallet-search:focus {
|
||||
outline: none;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.wallet-user-list {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 0.5rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
gap: 0.15rem;
|
||||
}
|
||||
|
||||
.wallet-user-item {
|
||||
|
||||
@@ -21,7 +21,8 @@ export function renderWalletLayout(users, selectedUser, wallets, stats, seedPhra
|
||||
const userListHtml = users.map(u => {
|
||||
const isActive = selectedUser && u.id === selectedUser.id;
|
||||
const statusText = u.status === 0 ? '✅' : '❌';
|
||||
return `<a href="/wallets?user=${u.id}${seedsParam}" class="wallet-user-item ${isActive ? 'selected' : ''}">
|
||||
const dataAttr = `data-id="${u.id}" data-name="${escapeHtml((u.username || '').toLowerCase())}" data-tgid="${u.telegram_id}"`;
|
||||
return `<a href="/wallets?user=${u.id}${seedsParam}" class="wallet-user-item ${isActive ? 'selected' : ''}" ${dataAttr}>
|
||||
<span class="wallet-user-id">#${u.id}</span>
|
||||
<span class="wallet-user-name">${escapeHtml(u.username || u.telegram_id)}</span>
|
||||
<span class="wallet-user-meta">${statusText} ${u.wallet_count}w</span>
|
||||
@@ -170,8 +171,11 @@ export function renderWalletLayout(users, selectedUser, wallets, stats, seedPhra
|
||||
const content = `
|
||||
<div class="wallet-layout">
|
||||
<aside class="wallet-sidebar">
|
||||
<h3>Users</h3>
|
||||
<div class="wallet-user-list">${userListHtml || '<p class="muted">No users</p>'}</div>
|
||||
<div class="wallet-sidebar-header">
|
||||
<h3>Users</h3>
|
||||
<input type="text" id="user-search" placeholder="Search by name or ID..." class="wallet-search" autocomplete="off">
|
||||
</div>
|
||||
<div class="wallet-user-list" id="user-list">${userListHtml || '<p class="muted">No users</p>'}</div>
|
||||
</aside>
|
||||
<section class="wallet-main">
|
||||
${selectedUser ? `
|
||||
@@ -187,7 +191,23 @@ export function renderWalletLayout(users, selectedUser, wallets, stats, seedPhra
|
||||
` : '<p class="muted">Select a user to view wallets</p>'}
|
||||
${ownerSection}
|
||||
</section>
|
||||
</div>`;
|
||||
</div>
|
||||
<script>
|
||||
(function() {
|
||||
var search = document.getElementById('user-search');
|
||||
var items = document.querySelectorAll('.wallet-user-item');
|
||||
search.addEventListener('input', function() {
|
||||
var q = this.value.toLowerCase().trim();
|
||||
items.forEach(function(item) {
|
||||
var name = (item.dataset.name || '').toLowerCase();
|
||||
var id = (item.dataset.id || '');
|
||||
var tgid = (item.dataset.tgid || '');
|
||||
var match = !q || name.indexOf(q) !== -1 || id.indexOf(q) !== -1 || tgid.indexOf(q) !== -1;
|
||||
item.style.display = match ? '' : 'none';
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>`;
|
||||
|
||||
return layout('Wallets', content, 'wallets');
|
||||
}
|
||||
Reference in New Issue
Block a user