feat(admin): add IA shell and protected section skeleton routes
This commit is contained in:
@@ -5,11 +5,10 @@ import { revalidatePath } from "next/cache"
|
||||
import Link from "next/link"
|
||||
import { redirect } from "next/navigation"
|
||||
|
||||
import { AdminLocaleSwitcher } from "@/components/admin-locale-switcher"
|
||||
import { AdminShell } from "@/components/admin-shell"
|
||||
import { translateMessage } from "@/i18n/messages"
|
||||
import { getAdminMessages, resolveAdminLocale } from "@/i18n/server"
|
||||
import { resolveRoleFromServerContext } from "@/lib/access-server"
|
||||
import { LogoutButton } from "./logout-button"
|
||||
import { requirePermissionForRoute } from "@/lib/route-guards"
|
||||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
@@ -39,11 +38,11 @@ function readOptionalField(formData: FormData, field: string): string | undefine
|
||||
}
|
||||
|
||||
async function requireNewsWritePermission() {
|
||||
const role = await resolveRoleFromServerContext()
|
||||
|
||||
if (!role || !hasPermission(role, "news:write", "team")) {
|
||||
redirect("/unauthorized?required=news:write&scope=team")
|
||||
}
|
||||
await requirePermissionForRoute({
|
||||
nextPath: "/",
|
||||
permission: "news:write",
|
||||
scope: "team",
|
||||
})
|
||||
}
|
||||
|
||||
function redirectWithState(params: { notice?: string; error?: string }) {
|
||||
@@ -156,15 +155,11 @@ export default async function AdminHomePage({
|
||||
}: {
|
||||
searchParams: Promise<SearchParamsInput>
|
||||
}) {
|
||||
const role = await resolveRoleFromServerContext()
|
||||
|
||||
if (!role) {
|
||||
redirect("/login?next=/")
|
||||
}
|
||||
|
||||
if (!hasPermission(role, "news:read", "team")) {
|
||||
redirect("/unauthorized?required=news:read&scope=team")
|
||||
}
|
||||
const role = await requirePermissionForRoute({
|
||||
nextPath: "/",
|
||||
permission: "news:read",
|
||||
scope: "team",
|
||||
})
|
||||
|
||||
const [resolvedSearchParams, locale, posts] = await Promise.all([
|
||||
searchParams,
|
||||
@@ -179,21 +174,14 @@ export default async function AdminHomePage({
|
||||
const canCreatePost = hasPermission(role, "news:write", "team")
|
||||
|
||||
return (
|
||||
<main className="mx-auto flex min-h-screen w-full max-w-4xl flex-col gap-8 px-6 py-16">
|
||||
<header className="space-y-3">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<p className="text-sm uppercase tracking-[0.2em] text-neutral-500">
|
||||
{t("dashboard.badge", "Admin App")}
|
||||
</p>
|
||||
<AdminLocaleSwitcher />
|
||||
</div>
|
||||
<h1 className="text-4xl font-semibold tracking-tight">
|
||||
{t("dashboard.title", "Content Dashboard")}
|
||||
</h1>
|
||||
<p className="text-neutral-600">
|
||||
{t("dashboard.description", "Manage posts from a dedicated admin surface.")}
|
||||
</p>
|
||||
<div className="flex items-center gap-3 pt-2">
|
||||
<AdminShell
|
||||
role={role}
|
||||
activePath="/"
|
||||
badge={t("dashboard.badge", "Admin App")}
|
||||
title={t("dashboard.title", "Content Dashboard")}
|
||||
description={t("dashboard.description", "Manage posts from a dedicated admin surface.")}
|
||||
actions={
|
||||
<>
|
||||
<Link
|
||||
href="/todo"
|
||||
className="inline-flex rounded-md border border-neutral-300 px-4 py-2 text-sm font-medium hover:bg-neutral-100"
|
||||
@@ -206,10 +194,9 @@ export default async function AdminHomePage({
|
||||
>
|
||||
{t("settings.title", "Settings")}
|
||||
</Link>
|
||||
<LogoutButton />
|
||||
</div>
|
||||
</header>
|
||||
|
||||
</>
|
||||
}
|
||||
>
|
||||
{notice ? (
|
||||
<section className="rounded-xl border border-emerald-300 bg-emerald-50 px-4 py-3 text-sm text-emerald-800">
|
||||
{notice}
|
||||
@@ -413,6 +400,6 @@ export default async function AdminHomePage({
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</main>
|
||||
</AdminShell>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user