// Auth check const token = localStorage.getItem('admin_token'); const user = JSON.parse(localStorage.getItem('admin_user') || '{}'); if (!token) { window.location.href = '/admin/login.html'; } // Display admin name document.getElementById('admin-name').textContent = user.email || 'Admin'; // Logout document.getElementById('logout-btn').addEventListener('click', () => { localStorage.removeItem('admin_token'); localStorage.removeItem('admin_user'); window.location.href = '/admin/login.html'; }); // Navigation const navLinks = document.querySelectorAll('.nav-link'); const sections = { 'overview': document.getElementById('overview-section'), 'moderation': document.getElementById('moderation-section'), 'users': document.getElementById('users-section'), 'documents': document.getElementById('documents-section') }; navLinks.forEach(link => { link.addEventListener('click', (e) => { const href = link.getAttribute('href'); // Only handle hash-based navigation (internal sections) // Let full URLs navigate normally if (!href || !href.startsWith('#')) { return; // Allow default navigation } e.preventDefault(); const section = href.substring(1); // Update active link navLinks.forEach(l => l.classList.remove('active', 'bg-blue-100', 'text-blue-700')); link.classList.add('active', 'bg-blue-100', 'text-blue-700'); // Show section Object.values(sections).forEach(s => s.classList.add('hidden')); if (sections[section]) { sections[section].classList.remove('hidden'); loadSection(section); } }); }); // API helper async function apiRequest(endpoint, options = {}) { const response = await fetch(endpoint, { ...options, headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json', ...options.headers } }); if (response.status === 401) { localStorage.removeItem('admin_token'); window.location.href = '/admin/login.html'; return; } return response.json(); } // Load statistics async function loadStatistics() { try { const response = await apiRequest('/api/admin/stats'); if (!response.success || !response.stats) { console.error('Invalid stats response:', response); return; } const stats = response.stats; document.getElementById('stat-documents').textContent = stats.documents?.total || 0; document.getElementById('stat-pending').textContent = stats.moderation?.total_pending || 0; document.getElementById('stat-approved').textContent = stats.blog?.published || 0; document.getElementById('stat-users').textContent = stats.users?.total || 0; } catch (error) { console.error('Failed to load statistics:', error); } } // Load recent activity async function loadRecentActivity() { const container = document.getElementById('recent-activity'); try { const response = await apiRequest('/api/admin/activity'); if (!response.success || !response.activity || response.activity.length === 0) { container.innerHTML = '
No recent activity
'; return; } container.innerHTML = response.activity.map(item => { // Generate description from activity data const action = item.action || 'reviewed'; const itemType = item.item_type || 'item'; const description = `${action.charAt(0).toUpperCase() + action.slice(1)} ${itemType}`; return `
${getActivityIcon(action)}

${description}

${formatDate(item.timestamp)}

`; }).join(''); } catch (error) { console.error('Failed to load activity:', error); container.innerHTML = '
Failed to load activity
'; } } // Load moderation queue async function loadModerationQueue(filter = 'all') { const container = document.getElementById('moderation-queue'); try { const response = await apiRequest(`/api/admin/moderation?type=${filter}`); if (!response.success || !response.items || response.items.length === 0) { container.innerHTML = '
No items pending review
'; return; } container.innerHTML = response.items.map(item => `
${item.type} ${formatDate(item.submitted_at)}

${item.title}

${truncate(item.content || item.description, 150)}

`).join(''); } catch (error) { console.error('Failed to load moderation queue:', error); container.innerHTML = '
Failed to load queue
'; } } // Load users async function loadUsers() { const container = document.getElementById('users-list'); try { const response = await apiRequest('/api/admin/users'); if (!response.success || !response.users || response.users.length === 0) { container.innerHTML = '
No users found
'; return; } container.innerHTML = response.users.map(user => `
${user.email.charAt(0).toUpperCase()}

${user.email}

Role: ${user.role}

${user.role} ${user._id !== user._id ? ` ` : ''}
`).join(''); } catch (error) { console.error('Failed to load users:', error); container.innerHTML = '
Failed to load users
'; } } // Load documents async function loadDocuments() { const container = document.getElementById('documents-list'); try { const response = await apiRequest('/api/documents'); if (!response.success || !response.documents || response.documents.length === 0) { container.innerHTML = '
No documents found
'; return; } container.innerHTML = response.documents.map(doc => `

${doc.title}

${doc.quadrant || 'No quadrant'} • ${formatDate(doc.created_at)}

View
`).join(''); } catch (error) { console.error('Failed to load documents:', error); container.innerHTML = '
Failed to load documents
'; } } // Load section data function loadSection(section) { switch (section) { case 'overview': loadStatistics(); loadRecentActivity(); break; case 'moderation': loadModerationQueue(); break; case 'users': loadUsers(); break; case 'documents': loadDocuments(); break; } } // Approve item async function approveItem(itemId) { if (!confirm('Approve this item?')) return; try { const response = await apiRequest(`/api/admin/moderation/${itemId}/approve`, { method: 'POST' }); if (response.success) { loadModerationQueue(); loadStatistics(); } else { alert('Failed to approve item'); } } catch (error) { console.error('Approval error:', error); alert('Failed to approve item'); } } // Reject item async function rejectItem(itemId) { if (!confirm('Reject this item?')) return; try { const response = await apiRequest(`/api/admin/moderation/${itemId}/reject`, { method: 'POST' }); if (response.success) { loadModerationQueue(); loadStatistics(); } else { alert('Failed to reject item'); } } catch (error) { console.error('Rejection error:', error); alert('Failed to reject item'); } } // Delete user async function deleteUser(userId) { if (!confirm('Delete this user? This action cannot be undone.')) return; try { const response = await apiRequest(`/api/admin/users/${userId}`, { method: 'DELETE' }); if (response.success) { loadUsers(); loadStatistics(); } else { alert(response.message || 'Failed to delete user'); } } catch (error) { console.error('Delete error:', error); alert('Failed to delete user'); } } // Delete document async function deleteDocument(docId) { if (!confirm('Delete this document? This action cannot be undone.')) return; try { const response = await apiRequest(`/api/documents/${docId}`, { method: 'DELETE' }); if (response.success) { loadDocuments(); loadStatistics(); } else { alert('Failed to delete document'); } } catch (error) { console.error('Delete error:', error); alert('Failed to delete document'); } } // Utility functions function getActivityColor(type) { const colors = { 'create': 'bg-green-500', 'update': 'bg-blue-500', 'delete': 'bg-red-500', 'approve': 'bg-purple-500' }; return colors[type] || 'bg-gray-500'; } function getActivityIcon(type) { const icons = { 'create': '+', 'update': '↻', 'delete': '×', 'approve': '✓' }; return icons[type] || '•'; } function formatDate(dateString) { if (!dateString) return 'Unknown'; const date = new Date(dateString); return date.toLocaleString('en-US', { month: 'short', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } function truncate(str, length) { if (!str) return ''; return str.length > length ? str.substring(0, length) + '...' : str; } // Queue filter document.getElementById('queue-filter')?.addEventListener('change', (e) => { loadModerationQueue(e.target.value); }); // Initialize loadStatistics(); loadRecentActivity(); // Make functions global for onclick handlers window.approveItem = approveItem; window.rejectItem = rejectItem; window.deleteUser = deleteUser; window.deleteDocument = deleteDocument;