first commit
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
import { EDITABLE_SECTIONS } from '@/lib/content/sections';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export function AdminShell({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
async function logout() {
|
||||
await fetch('/api/admin/logout', { method: 'POST' });
|
||||
router.replace('/admin/login');
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
return (
|
||||
<div dir="ltr" className="flex min-h-screen bg-base-900 text-slate-200">
|
||||
{/* Sidebar */}
|
||||
<aside className="hidden w-64 shrink-0 flex-col border-e border-white/8 bg-base-900/60 p-4 md:flex">
|
||||
<Link href="/admin" className="mb-6 flex items-center gap-2 px-2">
|
||||
<span className="grid h-8 w-8 place-items-center rounded-lg bg-electric/15 font-mono text-sm font-bold text-electric">
|
||||
SA
|
||||
</span>
|
||||
<span className="text-sm font-semibold text-white">Content CMS</span>
|
||||
</Link>
|
||||
|
||||
<nav className="flex flex-col gap-0.5">
|
||||
<SideLink href="/admin" active={pathname === '/admin'}>
|
||||
Dashboard
|
||||
</SideLink>
|
||||
<div className="mt-3 px-3 pb-1 font-mono text-[0.6rem] uppercase tracking-wider text-slate-600">
|
||||
Sections
|
||||
</div>
|
||||
{EDITABLE_SECTIONS.map((s) => {
|
||||
const href = `/admin/sections/${s.key}`;
|
||||
return (
|
||||
<SideLink key={s.key} href={href} active={pathname === href}>
|
||||
{s.label.en}
|
||||
</SideLink>
|
||||
);
|
||||
})}
|
||||
<div className="mt-3 px-3 pb-1 font-mono text-[0.6rem] uppercase tracking-wider text-slate-600">
|
||||
Content
|
||||
</div>
|
||||
<SideLink href="/admin/posts" active={pathname.startsWith('/admin/posts')}>
|
||||
Journal articles
|
||||
</SideLink>
|
||||
</nav>
|
||||
|
||||
<div className="mt-auto flex flex-col gap-1 pt-4">
|
||||
<a
|
||||
href="/"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="rounded-lg px-3 py-2 text-sm text-slate-400 transition-colors hover:bg-white/[0.04] hover:text-white"
|
||||
>
|
||||
View site ↗
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
onClick={logout}
|
||||
className="rounded-lg px-3 py-2 text-start text-sm text-slate-400 transition-colors hover:bg-white/[0.04] hover:text-white"
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Mobile top bar */}
|
||||
<div className="flex min-w-0 grow flex-col">
|
||||
<header className="flex items-center justify-between border-b border-white/8 px-5 py-3 md:hidden">
|
||||
<Link href="/admin" className="text-sm font-semibold text-white">
|
||||
Content CMS
|
||||
</Link>
|
||||
<button
|
||||
type="button"
|
||||
onClick={logout}
|
||||
className="text-sm text-slate-400 hover:text-white"
|
||||
>
|
||||
Log out
|
||||
</button>
|
||||
</header>
|
||||
|
||||
{/* Mobile section selector */}
|
||||
<nav className="flex gap-2 overflow-x-auto border-b border-white/8 px-5 py-2 md:hidden">
|
||||
<MobileChip href="/admin" active={pathname === '/admin'} label="Dashboard" />
|
||||
{EDITABLE_SECTIONS.map((s) => (
|
||||
<MobileChip
|
||||
key={s.key}
|
||||
href={`/admin/sections/${s.key}`}
|
||||
active={pathname === `/admin/sections/${s.key}`}
|
||||
label={s.label.en}
|
||||
/>
|
||||
))}
|
||||
<MobileChip
|
||||
href="/admin/posts"
|
||||
active={pathname.startsWith('/admin/posts')}
|
||||
label="Articles"
|
||||
/>
|
||||
</nav>
|
||||
|
||||
<main className="grow px-6 py-6 sm:px-8">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function SideLink({
|
||||
href,
|
||||
active,
|
||||
children,
|
||||
}: {
|
||||
href: string;
|
||||
active: boolean;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={cn(
|
||||
'rounded-lg px-3 py-2 text-sm transition-colors',
|
||||
active
|
||||
? 'bg-electric/12 font-medium text-electric'
|
||||
: 'text-slate-400 hover:bg-white/[0.04] hover:text-white',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function MobileChip({
|
||||
href,
|
||||
active,
|
||||
label,
|
||||
}: {
|
||||
href: string;
|
||||
active: boolean;
|
||||
label: string;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={cn(
|
||||
'shrink-0 rounded-full border px-3 py-1 text-xs transition-colors',
|
||||
active
|
||||
? 'border-electric/40 bg-electric/10 text-electric'
|
||||
: 'border-white/10 text-slate-400',
|
||||
)}
|
||||
>
|
||||
{label}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user