import { createPost, deletePost, listPostsWithTranslations, updatePost, upsertPostTranslation, } from "@cms/db" import { Button } from "@cms/ui/button" import { revalidatePath } from "next/cache" import { redirect } from "next/navigation" import { AdminShell } from "@/components/admin-shell" import { requirePermissionForRoute } from "@/lib/route-guards" export const dynamic = "force-dynamic" type SearchParamsInput = Record const SUPPORTED_LOCALES = ["de", "en", "es", "fr"] as const type SupportedLocale = (typeof SUPPORTED_LOCALES)[number] function readFirstValue(value: string | string[] | undefined): string | null { if (Array.isArray(value)) { return value[0] ?? null } return value ?? null } function readInputString(formData: FormData, field: string): string { const value = formData.get(field) return typeof value === "string" ? value.trim() : "" } function readNullableString(formData: FormData, field: string): string | undefined { const value = readInputString(formData, field) return value.length > 0 ? value : undefined } function normalizeLocale(input: string | null): SupportedLocale { if (input && SUPPORTED_LOCALES.includes(input as SupportedLocale)) { return input as SupportedLocale } return "en" } function redirectWithState(params: { notice?: string; error?: string }) { const query = new URLSearchParams() if (params.notice) { query.set("notice", params.notice) } if (params.error) { query.set("error", params.error) } const value = query.toString() redirect(value ? `/news?${value}` : "/news") } async function createNewsAction(formData: FormData) { "use server" await requirePermissionForRoute({ nextPath: "/news", permission: "news:write", scope: "team", }) try { await createPost({ title: readInputString(formData, "title"), slug: readInputString(formData, "slug"), excerpt: readNullableString(formData, "excerpt"), body: readInputString(formData, "body"), status: readInputString(formData, "status") === "published" ? "published" : "draft", }) } catch { redirectWithState({ error: "Failed to create post." }) } revalidatePath("/news") revalidatePath("/") redirectWithState({ notice: "Post created." }) } async function updateNewsAction(formData: FormData) { "use server" await requirePermissionForRoute({ nextPath: "/news", permission: "news:write", scope: "team", }) try { await updatePost(readInputString(formData, "id"), { title: readInputString(formData, "title"), slug: readInputString(formData, "slug"), excerpt: readNullableString(formData, "excerpt"), body: readInputString(formData, "body"), status: readInputString(formData, "status") === "published" ? "published" : "draft", }) } catch { redirectWithState({ error: "Failed to update post." }) } revalidatePath("/news") revalidatePath("/") redirectWithState({ notice: "Post updated." }) } async function deleteNewsAction(formData: FormData) { "use server" await requirePermissionForRoute({ nextPath: "/news", permission: "news:write", scope: "team", }) try { await deletePost(readInputString(formData, "id")) } catch { redirectWithState({ error: "Failed to delete post." }) } revalidatePath("/news") revalidatePath("/") redirectWithState({ notice: "Post deleted." }) } async function upsertNewsTranslationAction(formData: FormData) { "use server" await requirePermissionForRoute({ nextPath: "/news", permission: "news:write", scope: "team", }) const locale = normalizeLocale(readInputString(formData, "locale")) try { await upsertPostTranslation({ postId: readInputString(formData, "postId"), locale, title: readInputString(formData, "title"), excerpt: readNullableString(formData, "excerpt") ?? null, body: readInputString(formData, "body"), }) } catch { redirectWithState({ error: "Failed to save translation." }) } revalidatePath("/news") revalidatePath("/") redirectWithState({ notice: "Post translation saved." }) } export default async function NewsManagementPage({ searchParams, }: { searchParams: Promise }) { const role = await requirePermissionForRoute({ nextPath: "/news", permission: "news:read", scope: "team", }) const [resolvedSearchParams, posts] = await Promise.all([ searchParams, listPostsWithTranslations(), ]) const notice = readFirstValue(resolvedSearchParams.notice) const error = readFirstValue(resolvedSearchParams.error) const selectedLocale = normalizeLocale(readFirstValue(resolvedSearchParams.locale)) return ( {notice ? (
{notice}
) : null} {error ? (
{error}
) : null}

Create Post