TG almost works

This commit is contained in:
AB from home.homenet
2025-10-19 04:13:36 +03:00
parent 42c8016d9c
commit d972f10f83
31 changed files with 3302 additions and 427 deletions

View File

@@ -906,6 +906,103 @@
color: #1d1d1f;
font-family: 'SF Mono', Monaco, 'Cascadia Code', 'Roboto Mono', Consolas, 'Courier New', monospace;
}
/* User Requests Styles */
.request-cards {
display: flex;
flex-direction: column;
gap: 16px;
}
.request-card {
border: 1px solid #e0e0e0;
border-radius: 8px;
padding: 20px;
background: #fafafa;
}
.request-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.request-header h4 {
margin: 0;
font-size: 18px;
font-weight: 600;
}
.request-info {
margin-bottom: 16px;
}
.request-info p {
margin: 8px 0;
color: #666;
}
.request-actions {
display: flex;
gap: 12px;
}
.badge {
display: inline-flex;
align-items: center;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.badge-warning {
background: #ffc107;
color: #000;
}
.badge-success {
background: #28a745;
color: white;
}
.badge-danger {
background: #dc3545;
color: white;
}
.badge-secondary {
background: #6c757d;
color: white;
}
.button-success {
background: #28a745;
color: white;
border: none;
}
.button-success:hover {
background: #218838;
}
.button-danger {
background: #dc3545;
color: white;
border: none;
}
.button-danger:hover {
background: #c82333;
}
.button-small {
padding: 6px 12px;
font-size: 14px;
}
</style>
</head>
<body>
@@ -940,6 +1037,9 @@
<li class="nav-item">
<a href="#telegram" class="nav-link" onclick="showPage('telegram')">Telegram Bot</a>
</li>
<li class="nav-item">
<a href="#user-requests" class="nav-link" onclick="showPage('user-requests')">User Requests</a>
</li>
</ul>
</nav>
</aside>
@@ -1293,6 +1393,57 @@
</div>
</div>
</section>
<!-- User Requests -->
<section id="user-requests" class="page-section">
<div class="page-header">
<h1 class="page-title">User Requests</h1>
<p class="page-subtitle">Manage access requests from Telegram users</p>
</div>
<!-- Request Status Overview -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Request Overview</h2>
</div>
<div class="card-body">
<div class="stats-grid" id="requestStats">
<div class="stat-card">
<div class="stat-value" id="pendingRequests">0</div>
<div class="stat-label">Pending</div>
</div>
<div class="stat-card">
<div class="stat-value" id="approvedRequests">0</div>
<div class="stat-label">Approved</div>
</div>
<div class="stat-card">
<div class="stat-value" id="declinedRequests">0</div>
<div class="stat-label">Declined</div>
</div>
</div>
</div>
</div>
<!-- Pending Requests -->
<div class="card">
<div class="card-header">
<h2 class="card-title">Pending Requests</h2>
<button class="button button-outline" onclick="loadUserRequests()">
<span class="icon">🔄</span>
Refresh
</button>
</div>
<div id="pendingRequestsTable" class="loading">Loading...</div>
</div>
<!-- All Requests -->
<div class="card">
<div class="card-header">
<h2 class="card-title">All Requests</h2>
</div>
<div id="allRequestsTable" class="loading">Loading...</div>
</div>
</section>
</main>
</div>
@@ -2966,11 +3117,246 @@
}
});
// User Requests Functions
async function loadUserRequests() {
await loadRequestStats();
await loadPendingRequests();
await loadAllRequests();
}
async function loadRequestStats() {
try {
const response = await fetch(`${API_BASE}/user-requests`);
if (response.ok) {
const data = await response.json();
const requests = data.items || [];
const stats = {
pending: requests.filter(r => r.status === 'pending').length,
approved: requests.filter(r => r.status === 'approved').length,
declined: requests.filter(r => r.status === 'declined').length
};
document.getElementById('pendingRequests').textContent = stats.pending;
document.getElementById('approvedRequests').textContent = stats.approved;
document.getElementById('declinedRequests').textContent = stats.declined;
}
} catch (error) {
console.error('Error loading request stats:', error);
}
}
async function loadPendingRequests() {
try {
const response = await fetch(`${API_BASE}/user-requests?status=pending`);
if (response.ok) {
const data = await response.json();
renderPendingRequests(data.items || []);
} else {
document.getElementById('pendingRequestsTable').innerHTML = '<p class="error-message">Failed to load pending requests</p>';
}
} catch (error) {
console.error('Error loading pending requests:', error);
document.getElementById('pendingRequestsTable').innerHTML = '<p class="error-message">Error loading pending requests</p>';
}
}
async function loadAllRequests() {
try {
const response = await fetch(`${API_BASE}/user-requests`);
if (response.ok) {
const data = await response.json();
renderAllRequests(data.items || []);
} else {
document.getElementById('allRequestsTable').innerHTML = '<p class="error-message">Failed to load requests</p>';
}
} catch (error) {
console.error('Error loading requests:', error);
document.getElementById('allRequestsTable').innerHTML = '<p class="error-message">Error loading requests</p>';
}
}
function renderPendingRequests(requests) {
const container = document.getElementById('pendingRequestsTable');
if (requests.length === 0) {
container.innerHTML = '<p class="text-muted">No pending requests</p>';
return;
}
const html = `
<div class="request-cards">
${requests.map(request => `
<div class="request-card">
<div class="request-header">
<h4>${escapeHtml(request.full_name)}</h4>
<span class="badge badge-warning">Pending</span>
</div>
<div class="request-info">
<p>📱 Telegram: ${request.telegram_username ? '@' + escapeHtml(request.telegram_username) : 'ID: ' + request.telegram_id}</p>
<p>📅 Requested: ${new Date(request.created_at).toLocaleString()}</p>
${request.request_message ? `<p>💬 Message: ${escapeHtml(request.request_message)}</p>` : ''}
</div>
<div class="request-actions">
<button class="button button-success" onclick="approveRequest('${request.id}')">
✅ Approve
</button>
<button class="button button-danger" onclick="declineRequest('${request.id}')">
❌ Decline
</button>
</div>
</div>
`).join('')}
</div>
`;
container.innerHTML = html;
container.classList.remove('loading');
}
function renderAllRequests(requests) {
const container = document.getElementById('allRequestsTable');
if (requests.length === 0) {
container.innerHTML = '<p class="text-muted">No requests found</p>';
return;
}
const html = `
<div class="table-responsive">
<table class="data-table">
<thead>
<tr>
<th>Name</th>
<th>Telegram</th>
<th>Status</th>
<th>Requested</th>
<th>Processed By</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
${requests.map(request => `
<tr>
<td>${escapeHtml(request.full_name)}</td>
<td>${request.telegram_username ? '@' + escapeHtml(request.telegram_username) : 'ID: ' + request.telegram_id}</td>
<td>
<span class="badge badge-${getStatusBadgeClass(request.status)}">
${escapeHtml(request.status)}
</span>
</td>
<td>${new Date(request.created_at).toLocaleString()}</td>
<td>${request.processed_at ? new Date(request.processed_at).toLocaleString() : '-'}</td>
<td>
${request.status === 'pending' ? `
<button class="button button-small button-success" onclick="approveRequest('${request.id}')">
Approve
</button>
<button class="button button-small button-danger" onclick="declineRequest('${request.id}')">
Decline
</button>
` : `
<button class="button button-small button-outline" onclick="deleteRequest('${request.id}')">
Delete
</button>
`}
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
container.innerHTML = html;
container.classList.remove('loading');
}
function getStatusBadgeClass(status) {
switch (status) {
case 'pending': return 'warning';
case 'approved': return 'success';
case 'declined': return 'danger';
default: return 'secondary';
}
}
async function approveRequest(requestId) {
const message = prompt('Optional message for the user:');
try {
const response = await fetch(`${API_BASE}/user-requests/${requestId}/approve`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ response_message: message })
});
if (response.ok) {
showAlert('Request approved successfully', 'success');
await loadUserRequests();
} else {
const error = await response.text();
showAlert('Failed to approve request: ' + error, 'error');
}
} catch (error) {
showAlert('Error approving request: ' + error.message, 'error');
}
}
async function declineRequest(requestId) {
const message = prompt('Reason for declining (optional):');
if (message === null) return; // User cancelled
try {
const response = await fetch(`${API_BASE}/user-requests/${requestId}/decline`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ response_message: message })
});
if (response.ok) {
showAlert('Request declined', 'info');
await loadUserRequests();
} else {
const error = await response.text();
showAlert('Failed to decline request: ' + error, 'error');
}
} catch (error) {
showAlert('Error declining request: ' + error.message, 'error');
}
}
async function deleteRequest(requestId) {
if (!confirm('Are you sure you want to delete this request?')) return;
try {
const response = await fetch(`${API_BASE}/user-requests/${requestId}`, {
method: 'DELETE'
});
if (response.ok) {
showAlert('Request deleted', 'success');
await loadUserRequests();
} else {
showAlert('Failed to delete request', 'error');
}
} catch (error) {
showAlert('Error deleting request: ' + error.message, 'error');
}
}
// Update loadPageData function to include telegram
const originalLoadPageData = window.loadPageData;
window.loadPageData = function(page) {
if (page === 'telegram') {
loadTelegram();
} else if (page === 'user-requests') {
loadUserRequests();
} else if (originalLoadPageData) {
originalLoadPageData(page);
}