document.addEventListener('DOMContentLoaded', () => { loadStats(); loadUsers(); initCollapsibles(); initSearch(); }); let selectedUserId = null; let selectedNotepadId = null; let allUsers = []; // Store users for client-side searching async function loadUsers() { const userListDiv = document.getElementById('user-list'); userListDiv.innerHTML = '

Loading users...

'; try { const res = await fetch('/admin/users'); if (res.status === 403) { window.location.href = '/login.html'; return; } allUsers = await res.json(); renderUsers(allUsers); } catch (err) { console.error(err); userListDiv.innerHTML = '

Error loading users.

'; } } function renderUsers(users) { const userListDiv = document.getElementById('user-list'); userListDiv.innerHTML = ''; if (users.length === 0) { userListDiv.innerHTML = '

No users found.

'; return; } users.forEach(user => { const userDiv = document.createElement('div'); userDiv.className = 'user-item'; // Handle "Unowned" user display specifically or generic const ipDisplay = user.id === 0 ? '' : `
IP: ${escapeHtml(user.last_ip || 'Unknown')}`; const infoDiv = document.createElement('div'); infoDiv.innerHTML = `${escapeHtml(user.email)} (${escapeHtml(user.first_name)} ${escapeHtml(user.last_name)}) ${ipDisplay}`; const actionsDiv = document.createElement('div'); // Ban IP Button (only for real users with IPs) if (user.last_ip && user.id !== 0) { const banIpBtn = document.createElement('button'); banIpBtn.className = 'admin-button'; banIpBtn.textContent = 'Ban IP'; banIpBtn.onclick = (e) => { e.stopPropagation(); banUserIp(user.id); }; actionsDiv.appendChild(banIpBtn); } userDiv.appendChild(infoDiv); userDiv.appendChild(actionsDiv); userDiv.addEventListener('click', () => { selectedUserId = user.id; loadNotepads(user.id); expandSection('notepad-list'); }); userListDiv.appendChild(userDiv); }); } function initSearch() { const searchInput = document.getElementById('user-search'); searchInput.addEventListener('input', (e) => { const term = e.target.value.toLowerCase(); const filtered = allUsers.filter(user => (user.email && user.email.toLowerCase().includes(term)) || (user.first_name && user.first_name.toLowerCase().includes(term)) || (user.last_name && user.last_name.toLowerCase().includes(term)) ); renderUsers(filtered); }); } async function loadNotepads(userId) { const notepadListDiv = document.getElementById('notepad-list'); notepadListDiv.innerHTML = '

Loading notepads...

'; try { const res = await fetch(`/admin/users/${userId}/notepads`); const notepads = await res.json(); notepadListDiv.innerHTML = ''; notepads.forEach(notepad => { const notepadDiv = document.createElement('div'); notepadDiv.className = 'user-item'; const infoDiv = document.createElement('div'); infoDiv.innerHTML = `${escapeHtml(notepad.name)} (Public: ${notepad.is_public})`; const deleteBtn = document.createElement('button'); deleteBtn.className = 'admin-button'; deleteBtn.textContent = 'Delete Pad'; deleteBtn.onclick = async (e) => { e.stopPropagation(); if(confirm('Delete this entire notepad and all its notes?')) { await deleteNotepad(notepad.id); loadNotepads(userId); } }; notepadDiv.appendChild(infoDiv); notepadDiv.appendChild(deleteBtn); notepadDiv.addEventListener('click', () => { selectedNotepadId = notepad.id; loadNotes(notepad.id); expandSection('note-list'); }); notepadListDiv.appendChild(notepadDiv); }); } catch (err) { console.error(err); notepadListDiv.innerHTML = '

Error loading notepads.

'; } } async function loadNotes(notepadId) { const noteListDiv = document.getElementById('note-list'); noteListDiv.innerHTML = '

Loading notes...

'; try { const res = await fetch(`/admin/notepads/${notepadId}/notes`); const notes = await res.json(); noteListDiv.innerHTML = ''; notes.forEach(note => { const noteDiv = document.createElement('div'); noteDiv.className = 'user-item'; const infoDiv = document.createElement('div'); infoDiv.innerHTML = `${escapeHtml(note.title)}: ${escapeHtml(note.content)}`; noteDiv.appendChild(infoDiv); // Add delete button for each note const deleteButton = document.createElement('button'); deleteButton.className = 'admin-button'; deleteButton.textContent = 'Delete Note'; deleteButton.addEventListener('click', async (e) => { e.stopPropagation(); // Prevent selecting the note if (confirm('Are you sure you want to delete this note?')) { await deleteNote(note.id); loadNotes(notepadId); // Refresh notes after deletion } }); noteDiv.appendChild(deleteButton); noteListDiv.appendChild(noteDiv); }); } catch (err) { console.error(err); noteListDiv.innerHTML = '

Error loading notes.

'; } } async function deleteNote(noteId) { try { const res = await fetch(`/admin/notes/${noteId}`, { method: 'DELETE' }); if (res.ok) { alert('Note deleted successfully.'); } else { alert('Failed to delete note.'); } } catch (err) { console.error(err); alert('Error deleting note.'); } } async function deleteNotepad(notepadId) { try { const res = await fetch(`/admin/notepads/${notepadId}`, { method: 'DELETE' }); if (res.ok) { alert('Notepad deleted.'); } else { alert('Failed to delete notepad.'); } } catch (err) { console.error(err); } } async function banUserIp(userId) { if(!confirm("Are you sure you want to ban this IP address? This will block all future access from this IP.")) return; try { const res = await fetch(`/admin/users/${userId}/ban_ip`, { method: 'POST' }); const data = await res.json(); alert(data.message || data.error); } catch (err) { console.error(err); alert("Error banning IP"); } } async function loadStats() { const container = document.getElementById('admin-stats'); try { const res = await fetch('/admin/stats'); if (res.status === 403) { window.location.href = '/login.html'; return; } const stats = await res.json(); container.innerHTML = `

${stats.user_count}

Users

${stats.notepad_count}

Notepads

${stats.note_count}

Notes

`; } catch (err) { console.error(err); container.innerHTML = ''; } } function initCollapsibles() { const headers = document.querySelectorAll('.section-title'); headers.forEach(header => { header.addEventListener('click', () => { const content = header.nextElementSibling; content.classList.toggle('collapsed'); }); }); } function expandSection(id) { const el = document.getElementById(id); if(el) el.classList.remove('collapsed'); } function escapeHtml(text) { if (text === null || text === undefined) return ''; return String(text) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); }