115 lines
4.3 KiB
HTML
115 lines
4.3 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Admin Dashboard - {{ cfg.site.name }}{% endblock %}
|
|
|
|
{% block nav_actions %}
|
|
<span class="nav-user">Logged in as Admin</span>
|
|
<a href="{{ url_for('admin_logout') }}" class="nav-btn">Logout</a>
|
|
<a href="{{ url_for('index') }}" class="nav-btn nav-btn-save">New Paste</a>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="admin-container">
|
|
<div class="admin-header">
|
|
<h1>Global Paste Management</h1>
|
|
<p>Active Pastes: <strong>{{ active_count }}</strong> | Total in DB (inc. expired): {{ pastes|length }}</p>
|
|
</div>
|
|
|
|
<div class="admin-bulk-bar">
|
|
<button class="btn-bulk-delete" id="bulkDeleteBtn" disabled>Delete Selected</button>
|
|
<span class="admin-bulk-count" id="bulkCount">0 selected</span>
|
|
</div>
|
|
|
|
<div class="table-responsive">
|
|
<table class="admin-table" id="adminTable">
|
|
<thead>
|
|
<tr>
|
|
<th class="check-col"><input type="checkbox" id="selectAll" title="Select all"></th>
|
|
<th>ID</th>
|
|
<th>Created</th>
|
|
<th>Expires</th>
|
|
<th>Views</th>
|
|
<th>Actions</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for paste in pastes %}
|
|
<tr>
|
|
<td class="check-col"><input type="checkbox" class="row-check" value="{{ paste.id }}"></td>
|
|
<td><a href="{{ url_for('view_paste', paste_id=paste.id) }}" target="_blank"><code>{{ paste.id }}</code></a></td>
|
|
<td>{{ paste.created_at }}</td>
|
|
<td>{{ paste.expires_at or 'Never' }}</td>
|
|
<td>{{ paste.views }}</td>
|
|
<td>
|
|
<form action="{{ url_for('admin_delete_paste', paste_id=paste.id) }}" method="POST" onsubmit="return confirm('Delete this paste permanently?')">
|
|
<button type="submit" class="btn-delete-small">Delete</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<script nonce="{{ csp_nonce }}">
|
|
(function () {
|
|
const selectAll = document.getElementById('selectAll');
|
|
const bulkDeleteBtn = document.getElementById('bulkDeleteBtn');
|
|
const bulkCount = document.getElementById('bulkCount');
|
|
|
|
function getChecked() {
|
|
return [...document.querySelectorAll('.row-check:checked')];
|
|
}
|
|
|
|
function updateBulkBar() {
|
|
const checked = getChecked();
|
|
bulkCount.textContent = checked.length + ' selected';
|
|
bulkDeleteBtn.disabled = checked.length === 0;
|
|
const all = document.querySelectorAll('.row-check');
|
|
selectAll.indeterminate = checked.length > 0 && checked.length < all.length;
|
|
selectAll.checked = all.length > 0 && checked.length === all.length;
|
|
}
|
|
|
|
document.querySelectorAll('.row-check').forEach(cb =>
|
|
cb.addEventListener('change', updateBulkBar)
|
|
);
|
|
|
|
selectAll.addEventListener('change', function () {
|
|
document.querySelectorAll('.row-check').forEach(cb => cb.checked = this.checked);
|
|
updateBulkBar();
|
|
});
|
|
|
|
bulkDeleteBtn.addEventListener('click', async function () {
|
|
const checked = getChecked();
|
|
if (!checked.length) return;
|
|
if (!confirm('Delete ' + checked.length + ' paste(s) permanently?')) return;
|
|
|
|
bulkDeleteBtn.disabled = true;
|
|
bulkDeleteBtn.textContent = 'Deleting…';
|
|
|
|
const ids = checked.map(cb => cb.value);
|
|
try {
|
|
const resp = await fetch('{{ url_for("admin_bulk_delete") }}', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ ids }),
|
|
});
|
|
if (resp.ok) {
|
|
window.location.reload();
|
|
} else {
|
|
const data = await resp.json().catch(() => ({}));
|
|
alert('Error: ' + (data.error || resp.status));
|
|
bulkDeleteBtn.disabled = false;
|
|
bulkDeleteBtn.textContent = 'Delete Selected';
|
|
}
|
|
} catch (e) {
|
|
alert('Request failed. Please try again.');
|
|
bulkDeleteBtn.disabled = false;
|
|
bulkDeleteBtn.textContent = 'Delete Selected';
|
|
}
|
|
});
|
|
})();
|
|
</script>
|
|
{% endblock %}
|