mirror of
https://github.com/house-of-vanity/OutFleet.git
synced 2025-10-24 01:09:08 +00:00
TG almost works
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user