101 lines
3.8 KiB
TypeScript
101 lines
3.8 KiB
TypeScript
import { setRequestLocale, getTranslations } from 'next-intl/server';
|
|
import { signOut } from '@/auth';
|
|
import { getSafeSession } from '@/lib/safeAuth';
|
|
import { Link } from '@/i18n/routing';
|
|
import { LanguageSwitcher } from '@/components/LanguageSwitcher';
|
|
|
|
export default async function AdminLayout({
|
|
children,
|
|
params,
|
|
}: {
|
|
children: React.ReactNode;
|
|
params: Promise<{ locale: string }>;
|
|
}) {
|
|
const { locale } = await params;
|
|
setRequestLocale(locale);
|
|
const session = await getSafeSession();
|
|
const t = await getTranslations('admin');
|
|
const tr = await getTranslations('adminReplacements');
|
|
|
|
// Public login page is handled in admin/login/page.tsx — but layout still wraps it.
|
|
// For non-login admin routes we redirect when not signed in via a route segment guard.
|
|
// Here we expose `session` to the rendered children via a server util; simpler:
|
|
// we redirect from this layout only when path is NOT /admin/login. Since segment
|
|
// info isn't easily accessible, we let each page check itself. Login page will not redirect.
|
|
// We do the protection by rendering the nav only when signed in; pages must call requireAdmin().
|
|
|
|
return (
|
|
<div className="min-h-screen bg-ink-50">
|
|
<header className="border-b border-brand-700 bg-brand-600 text-white">
|
|
<div className="mx-auto flex max-w-6xl items-center justify-between gap-3 px-4 py-3">
|
|
<div className="flex items-center gap-3">
|
|
<Link href="/admin" className="text-base font-semibold">
|
|
{t('title')}
|
|
</Link>
|
|
{session?.user && (
|
|
<nav className="hidden gap-1 sm:flex">
|
|
<Link
|
|
href="/admin"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.bookings')}
|
|
</Link>
|
|
<Link
|
|
href="/admin/products"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.products')}
|
|
</Link>
|
|
<Link
|
|
href="/admin/pickup-slots"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.pickupSlots')}
|
|
</Link>
|
|
<Link
|
|
href="/admin/replacements"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{tr('navTitle')}
|
|
</Link>
|
|
<Link
|
|
href="/admin/settings"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.settings')}
|
|
</Link>
|
|
<Link
|
|
href="/admin/users"
|
|
className="rounded-md px-3 py-1.5 text-sm text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.users')}
|
|
</Link>
|
|
</nav>
|
|
)}
|
|
</div>
|
|
<div className="flex items-center gap-3">
|
|
<LanguageSwitcher />
|
|
{session?.user && (
|
|
<form
|
|
action={async () => {
|
|
'use server';
|
|
await signOut({ redirectTo: '/admin/login' });
|
|
}}
|
|
>
|
|
<button
|
|
type="submit"
|
|
className="rounded-md px-3 py-1.5 text-xs text-white/80 hover:bg-white/10 hover:text-white"
|
|
>
|
|
{t('nav.signOut')}
|
|
</button>
|
|
</form>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<main className="mx-auto max-w-6xl px-4 py-6">{children}</main>
|
|
</div>
|
|
);
|
|
}
|
|
|