<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SkyCode Admin</title>
<link href="https://cdn.jsdelivr.net/npm/daisyui@5" rel="stylesheet" type="text/css" />
<link href="https://cdn.jsdelivr.net/npm/daisyui@5/themes.css" rel="stylesheet" type="text/css" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
</head>
<body class="bg-base-200 min-h-screen">
<!-- Navbar -->
<div class="navbar bg-base-300 border-b border-base-100 h-12 min-h-12">
<div class="flex-1">
<a href="/" class="btn btn-ghost btn-sm text-lg font-bold">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-5 h-5">
<path stroke-linecap="round" stroke-linejoin="round" d="M17.25 6.75 22.5 12l-5.25 5.25m-10.5 0L1.5 12l5.25-5.25m7.5-3-4.5 16.5" />
</svg>
SkyCode
</a>
<span class="badge badge-warning ml-2">Admin</span>
</div>
<div class="flex-none gap-2">
<a href="/editor" class="btn btn-ghost btn-sm">Editor</a>
{{with CurrentUser}}
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-ghost btn-sm gap-1">
<div class="w-6 rounded-full overflow-hidden">
<img alt="{{.Name}}" src="{{.Avatar}}" class="w-full h-full object-cover" />
</div>
<span class="text-xs text-base-content/70">@{{.Handle}}</span>
</div>
<ul tabindex="0" class="menu menu-sm dropdown-content bg-base-100 rounded-box z-[1] mt-3 w-52 p-2 shadow">
<li><a href="https://theskyscape.com/user/{{.Handle}}" target="_blank">View Profile</a></li>
<li><a href="/auth/logout">Logout</a></li>
</ul>
</div>
{{end}}
</div>
</div>
<!-- Main Content -->
<div class="container mx-auto p-6 max-w-6xl">
<h1 class="text-2xl font-bold mb-6">Database Administration</h1>
<!-- Tabs -->
<div class="tabs tabs-box mb-6">
<a class="tab tab-active" onclick="showTab('users')" id="tab-users">Users</a>
<a class="tab" onclick="showTab('files')" id="tab-files">Files</a>
<a class="tab" onclick="showTab('errors')" id="tab-errors">Errors</a>
</div>
<!-- Users Panel -->
<div id="panel-users">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold">Users</h2>
<div class="flex gap-2">
<button class="btn btn-sm btn-warning"
hx-post="/admin/users/cleanup-duplicates"
hx-target="#users-table"
hx-swap="innerHTML"
hx-confirm="Delete all duplicate users and users with empty handles?">
Cleanup Duplicates
</button>
<button class="btn btn-sm btn-ghost"
hx-get="/admin/users"
hx-target="#users-table"
hx-swap="innerHTML">
Refresh
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th>Avatar</th>
<th>Handle</th>
<th>Name</th>
<th>Email</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="users-table" hx-get="/admin/users" hx-trigger="load" hx-swap="innerHTML">
<tr><td colspan="6" class="text-center text-base-content/50">Loading...</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Files Panel -->
<div id="panel-files" class="hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold">Files</h2>
<button class="btn btn-sm btn-ghost"
hx-get="/admin/files"
hx-target="#files-table"
hx-swap="innerHTML">
Refresh
</button>
</div>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th>Owner ID</th>
<th>Path</th>
<th>Language</th>
<th>Size</th>
<th>Created</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="files-table">
<tr><td colspan="6" class="text-center text-base-content/50">Click Files tab to load...</td></tr>
</tbody>
</table>
</div>
</div>
<!-- Errors Panel -->
<div id="panel-errors" class="hidden">
<div class="flex justify-between items-center mb-4">
<h2 class="text-lg font-semibold">Error Logs</h2>
<div class="flex gap-2">
<button class="btn btn-sm btn-warning"
hx-post="/admin/errors/clear"
hx-target="#errors-table"
hx-swap="innerHTML"
hx-confirm="Clear all error logs?">
Clear All
</button>
<button class="btn btn-sm btn-ghost"
hx-get="/admin/errors"
hx-target="#errors-table"
hx-swap="innerHTML">
Refresh
</button>
</div>
</div>
<div class="overflow-x-auto">
<table class="table table-zebra w-full">
<thead>
<tr>
<th>Name</th>
<th>Message</th>
<th>Count</th>
<th>Last Seen</th>
<th>Actions</th>
</tr>
</thead>
<tbody id="errors-table">
<tr><td colspan="5" class="text-center text-base-content/50">Click Errors tab to load...</td></tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Edit User Modal Container -->
<div id="edit-form-container" class="fixed inset-0 bg-black/50 z-50 hidden items-center justify-center">
<!-- Edit form loaded here by HTMX -->
</div>
<script>
// Tab switching
function showTab(tab) {
document.getElementById('tab-users').classList.toggle('tab-active', tab === 'users');
document.getElementById('tab-files').classList.toggle('tab-active', tab === 'files');
document.getElementById('tab-errors').classList.toggle('tab-active', tab === 'errors');
document.getElementById('panel-users').classList.toggle('hidden', tab !== 'users');
document.getElementById('panel-files').classList.toggle('hidden', tab !== 'files');
document.getElementById('panel-errors').classList.toggle('hidden', tab !== 'errors');
// Load files on first switch to files tab
if (tab === 'files') {
const filesTable = document.getElementById('files-table');
if (!filesTable.dataset.loaded) {
htmx.ajax('GET', '/admin/files', {target: '#files-table', swap: 'innerHTML'});
filesTable.dataset.loaded = 'true';
}
}
// Load errors on first switch to errors tab
if (tab === 'errors') {
const errorsTable = document.getElementById('errors-table');
if (!errorsTable.dataset.loaded) {
htmx.ajax('GET', '/admin/errors', {target: '#errors-table', swap: 'innerHTML'});
errorsTable.dataset.loaded = 'true';
}
}
}
// Show modal when edit form is loaded
document.body.addEventListener('htmx:afterSwap', function(evt) {
if (evt.detail.target.id === 'edit-form-container') {
const container = document.getElementById('edit-form-container');
if (container.innerHTML.trim()) {
container.classList.remove('hidden');
container.classList.add('flex');
} else {
container.classList.add('hidden');
container.classList.remove('flex');
}
}
});
// Close modal when clicking backdrop
document.getElementById('edit-form-container').addEventListener('click', function(e) {
if (e.target === this) {
this.innerHTML = '';
this.classList.add('hidden');
this.classList.remove('flex');
}
});
</script>
</body>
</html>