Files
slot/next-app/apps/web/src/app/admin/layout.tsx
T
chpark 59001dbc5f Add 201 React deploy + admin catch-all + redesign + tests
Stack on 201: PG 17 + Next.js 15 (Docker) + nginx (/, /php-ref/)
Home: StatStrip (8 metrics), LiveActivity feed, refined Hero aurora
Admin: 80+ menu catch-all renderer + read-only legacy table queries
Auth/CRUD: fix narrowing in 6 action routes, fix wr_last varchar(19),
  fix back() new URL on missing referer
Verify: 50/50 PASS across 5 iterations of login + comment + good + scrap

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 02:44:18 +09:00

46 lines
2.1 KiB
TypeScript

import { redirect } from 'next/navigation';
import Link from 'next/link';
import { getCurrentSiteUser } from '@/lib/page-data';
import { ADMIN_MENU } from '@/lib/admin-menu';
export const dynamic = 'force-dynamic';
export default async function AdminLayout({ children }: { children: React.ReactNode }) {
const user = await getCurrentSiteUser();
if (!user) redirect('/login?next=/admin');
if ((user.level ?? 0) < 10) redirect('/?error=permission');
return (
<div className="grid items-start gap-4 lg:grid-cols-[260px_1fr]">
<aside className="rounded-2xl border border-neutral-100 bg-white p-3 shadow-[0_1px_2px_rgba(0,0,0,0.02)] lg:sticky lg:top-[180px] lg:max-h-[calc(100vh-200px)] lg:overflow-y-auto">
<div className="px-2 py-2">
<div className="text-[11px] font-bold uppercase tracking-widest text-brand-600">SLOT ADMIN</div>
<div className="mt-1 text-[14px] font-bold"> </div>
<div className="text-[11px] text-neutral-text-soft">{user.nick} · Lv.{user.level}</div>
</div>
{ADMIN_MENU.map((g) => (
<details key={g.code} open className="group mt-2">
<summary className="flex cursor-pointer items-center gap-1.5 rounded-lg px-2 py-1.5 text-[12px] font-bold text-neutral-700 hover:bg-brand-50">
<span className="text-base">{g.icon}</span>
<span>{g.label}</span>
<span className="ml-auto text-[10px] text-neutral-400 transition group-open:rotate-90"></span>
</summary>
<ul className="m-0 grid gap-0.5 p-0 pl-2 list-none">
{g.items.map((it) => (
<li key={it.slug}>
<Link
href={'/admin' + (it.slug ? '/' + it.slug : '')}
className="block rounded-lg px-2.5 py-1.5 text-[12.5px] text-neutral-700 hover:bg-brand-50 hover:text-brand-700"
>
{it.label}
</Link>
</li>
))}
</ul>
</details>
))}
</aside>
<section className="min-w-0">{children}</section>
</div>
);
}