refactor(admin): replace tab bar with sidebar layout
Some checks failed
CI / Backend (push) Successful in 38s
CI / UI (push) Successful in 42s
Release / Test backend (push) Successful in 40s
Release / Check ui (push) Successful in 33s
CI / Backend (pull_request) Successful in 28s
Release / Docker / caddy (push) Successful in 1m11s
CI / UI (pull_request) Successful in 29s
Release / Docker / ui (push) Successful in 1m54s
Release / Docker / runner (push) Successful in 4m27s
Release / Docker / backend (push) Failing after 5m11s
Release / Gitea Release (push) Has been skipped

Move admin navigation from a two-row tab strip into a persistent left
sidebar with grouped sections (Pages / Tools). Consolidate Scrape and
Audio entries in the global top nav into a single Admin link.
This commit is contained in:
Admin
2026-03-28 21:15:49 +05:00
parent dcf40197d4
commit b783dae5f4
2 changed files with 67 additions and 72 deletions

View File

@@ -257,20 +257,14 @@
<div class="ml-auto flex items-center gap-4"> <div class="ml-auto flex items-center gap-4">
<!-- Desktop: admin + profile + sign out (hidden on mobile) --> <!-- Desktop: admin + profile + sign out (hidden on mobile) -->
{#if data.user?.role === 'admin'} {#if data.user?.role === 'admin'}
<a <a
href="/admin/scrape" href="/admin/scrape"
class="hidden sm:block text-sm transition-colors {page.url.pathname.startsWith('/admin/scrape') ? 'text-zinc-100 font-medium' : 'text-zinc-400 hover:text-zinc-100'}" class="hidden sm:block text-sm transition-colors {page.url.pathname.startsWith('/admin') ? 'text-zinc-100 font-medium' : 'text-zinc-400 hover:text-zinc-100'}"
> >
Scrape Admin
</a> </a>
<a {/if}
href="/admin/audio"
class="hidden sm:block text-sm transition-colors {page.url.pathname.startsWith('/admin/audio') ? 'text-zinc-100 font-medium' : 'text-zinc-400 hover:text-zinc-100'}"
>
Audio
</a>
{/if}
<a <a
href="/profile" href="/profile"
class="hidden sm:block text-sm transition-colors {page.url.pathname === '/profile' ? 'text-zinc-100 font-medium' : 'text-zinc-400 hover:text-zinc-100'}" class="hidden sm:block text-sm transition-colors {page.url.pathname === '/profile' ? 'text-zinc-100 font-medium' : 'text-zinc-400 hover:text-zinc-100'}"
@@ -350,31 +344,17 @@
> >
Profile <span class="text-zinc-500 font-normal">({data.user.username})</span> Profile <span class="text-zinc-500 font-normal">({data.user.username})</span>
</a> </a>
{#if data.user?.role === 'admin'} {#if data.user?.role === 'admin'}
<div class="my-1 border-t border-zinc-700/60"></div> <div class="my-1 border-t border-zinc-700/60"></div>
<p class="px-3 pt-1 pb-0.5 text-xs text-zinc-600 uppercase tracking-widest">Admin</p> <p class="px-3 pt-1 pb-0.5 text-xs text-zinc-600 uppercase tracking-widest">Admin</p>
<a <a
href="/admin/scrape" href="/admin/scrape"
onclick={() => (menuOpen = false)} onclick={() => (menuOpen = false)}
class="px-3 py-2.5 rounded-lg text-sm font-medium transition-colors {page.url.pathname.startsWith('/admin/scrape') ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100'}" class="px-3 py-2.5 rounded-lg text-sm font-medium transition-colors {page.url.pathname.startsWith('/admin') ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100'}"
> >
Scrape tasks Admin panel
</a> </a>
<a {/if}
href="/admin/audio"
onclick={() => (menuOpen = false)}
class="px-3 py-2.5 rounded-lg text-sm font-medium transition-colors {page.url.pathname === '/admin/audio' ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100'}"
>
Audio cache
</a>
<a
href="/admin/audio-jobs"
onclick={() => (menuOpen = false)}
class="px-3 py-2.5 rounded-lg text-sm font-medium transition-colors {page.url.pathname.startsWith('/admin/audio-jobs') ? 'bg-zinc-800 text-zinc-100' : 'text-zinc-400 hover:bg-zinc-800 hover:text-zinc-100'}"
>
Audio jobs
</a>
{/if}
<div class="my-1 border-t border-zinc-700/60"></div> <div class="my-1 border-t border-zinc-700/60"></div>
<form method="POST" action="/logout"> <form method="POST" action="/logout">
<Button <Button

View File

@@ -1,12 +1,12 @@
<script lang="ts"> <script lang="ts">
import { page } from '$app/state'; import { page } from '$app/state';
const adminTabs = [ const internalLinks = [
{ href: '/admin/scrape', label: 'Scrape' }, { href: '/admin/scrape', label: 'Scrape' },
{ href: '/admin/audio', label: 'Audio' } { href: '/admin/audio', label: 'Audio' }
]; ];
const toolTabs = [ const externalLinks = [
{ href: 'https://feedback.libnovel.cc', label: 'Feedback' }, { href: 'https://feedback.libnovel.cc', label: 'Feedback' },
{ href: 'https://errors.libnovel.cc', label: 'Errors' }, { href: 'https://errors.libnovel.cc', label: 'Errors' },
{ href: 'https://analytics.libnovel.cc', label: 'Analytics' }, { href: 'https://analytics.libnovel.cc', label: 'Analytics' },
@@ -21,36 +21,51 @@
let { children }: Props = $props(); let { children }: Props = $props();
</script> </script>
<!-- Admin nav: internal pages + external tools --> <div class="flex min-h-[calc(100vh-4rem)] gap-0">
<div class="mb-6 flex flex-wrap items-center gap-3"> <!-- Sidebar -->
<!-- Internal admin pages --> <aside class="w-48 shrink-0 border-r border-zinc-800 px-3 py-6 flex flex-col gap-6">
<div class="flex gap-1 bg-zinc-800 rounded-lg p-1 border border-zinc-700"> <!-- Internal pages -->
{#each adminTabs as tab} <div>
<a <p class="px-2 mb-2 text-xs font-semibold text-zinc-600 uppercase tracking-widest">Pages</p>
href={tab.href} <nav class="flex flex-col gap-0.5">
class="px-4 py-1.5 rounded-md text-sm font-medium transition-colors {#each internalLinks as link}
{page.url.pathname.startsWith(tab.href) <a
? 'bg-zinc-700 text-zinc-100' href={link.href}
: 'text-zinc-400 hover:text-zinc-200'}" class="px-2 py-1.5 rounded-md text-sm font-medium transition-colors
> {page.url.pathname.startsWith(link.href)
{tab.label} ? 'bg-zinc-800 text-zinc-100'
</a> : 'text-zinc-400 hover:bg-zinc-800/60 hover:text-zinc-200'}"
{/each} >
</div> {link.label}
</a>
{/each}
</nav>
</div>
<!-- External tools (open in new tab) --> <!-- External tools -->
<div class="flex gap-1 bg-zinc-800 rounded-lg p-1 border border-zinc-700"> <div>
{#each toolTabs as tool} <p class="px-2 mb-2 text-xs font-semibold text-zinc-600 uppercase tracking-widest">Tools</p>
<a <nav class="flex flex-col gap-0.5">
href={tool.href} {#each externalLinks as link}
target="_blank" <a
rel="noopener noreferrer" href={link.href}
class="px-4 py-1.5 rounded-md text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors" target="_blank"
> rel="noopener noreferrer"
{tool.label} class="px-2 py-1.5 rounded-md text-sm font-medium text-zinc-400 hover:bg-zinc-800/60 hover:text-zinc-200 transition-colors flex items-center justify-between"
</a> >
{/each} {link.label}
</div> <svg class="w-3 h-3 shrink-0 opacity-50" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14" />
</svg>
</a>
{/each}
</nav>
</div>
</aside>
<!-- Main content -->
<main class="flex-1 min-w-0 px-8 py-6">
{@render children?.()}
</main>
</div> </div>
{@render children?.()}