import { ExternalLink, FolderKanban, ImageIcon, LayoutDashboard, LockKeyhole, LogOut, ShieldAlert, Shapes, SwatchBook, ArrowLeft, } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { Container } from "@/components/layout/container"; import { MotionFade } from "@/components/motion-fade"; import { DashboardCard } from "@/components/dashboard/dashboard-card"; import { DashboardLayout } from "@/components/dashboard/dashboard-layout"; import { ThemeToggle } from "@/components/theme-toggle"; import { Badge, type BadgeProps } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Separator } from "@/components/ui/separator"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { clearAdminSessionCookie, getAdminLockState, isAdminAuthConfigured, isAdminAuthenticated, isPasswordValid, registerFailedAdminAttempt, resetAdminFailedAttempts, setAdminSessionCookie, } from "@/lib/admin-auth"; import { getMaintenanceMode } from "@/lib/app-config"; import { getLocalizedPath } from "@/lib/locale"; import { getAdminMediaAssets } from "@/lib/media"; import { getAdminPortfolioCategories, getAdminPortfolioProjects, } from "@/lib/portfolio"; import { getRootNavigation } from "@/lib/root-navigation"; type RootPageProps = { searchParams?: { error?: string; }; }; export const dynamic = "force-dynamic"; const copy = { title: "Uebersicht", subtitle: "Interner Bereich fuer Kennzahlen und zentrale Navigation.", overview: "Uebersicht", maintenance: "Wartungsmodus", maintenanceTitle: "Wartungsmodus", maintenanceVisitorsClosed: "Website fuer Besucher geschlossen", maintenanceDescription: "Wenn aktiv, werden alle Seiten auf die Coming Soon Seite weitergeleitet.", maintenanceOpen: "Website fuer Besucher offen", maintenanceAction: "Zur Wartungsseite", uiKitTitle: "UI Kit", uiKitDescription: "Globale Referenz fuer Cards, Buttons, Inputs und Surface Levels.", uiKitAction: "Zur UI Kit", media: "Media", portfolio: "Portfolio", portfolioTitle: "Portfolio", portfolioDescription: "Kategorien, Projekte, Abschnitte und Dateien verwalten.", portfolioAction: "Zum Portfolio", mediaTitle: "Media Library", mediaDescription: "Uploads, externe URLs und Verwendungsorte zentral verwalten.", mediaAction: "Zur Media Library", loginTitle: "Root Login", loginText: "Nur autorisierte Nutzer duerfen diesen Bereich verwenden.", passwordLabel: "Passwort", loginButton: "Einloggen", invalidLogin: "Falsches Passwort.", lockedLogin: "Zu viele Fehlversuche. Bitte spaeter erneut versuchen.", configMissing: "ADMIN_PASSWORD und ADMIN_AUTH_SECRET fehlen in env.", basicAuthMissing: "ROOT_BASIC_AUTH_USER und ROOT_BASIC_AUTH_PASS fehlen in env.", logout: "Ausloggen", backToSite: "Zur Website", uiKit: "UI Kit", tableTitle: "Bereiche", tableDescription: "Direkter Zugriff auf alle Verwaltungsbereiche mit aktuellem Status.", tableSection: "Bereich", tableSummary: "Zusammenfassung", tableStatus: "Status", tableAction: "Aktion", maintenanceStatusOn: "Aktiv", maintenanceStatusOff: "Inaktiv", authStatusOk: "Bereit", authStatusWarning: "Pruefen", authCard: "Root Zugang", authDescription: "Admin Passwort und Session Signatur sind gesetzt.", authDescriptionWarning: "Eine oder mehrere Root Variablen fehlen und sollten geprueft werden.", mediaCountDescription: "Verfuegbare Dateien fuer Portfolio und Inhalte.", projectCountDescription: "Gespeicherte Portfolio Projekte im System.", categoryCountDescription: "Portfolio Kategorien mit eigener Sortierung.", systemStatusTitle: "Systemstatus", systemStatusDescription: "Technischer Zustand des Root Bereichs und der wichtigsten Inhaltsbereiche.", maintenanceLabel: "Wartung", contentLabel: "Inhalte", contentDescription: "Portfolio, Media und Kategorien sind direkt aus diesem Bereich erreichbar.", publishedProjects: "Veroeffentlicht", totalProjects: "Projekte", totalCategories: "Kategorien", totalMedia: "Dateien", }; export default async function RootPage({ searchParams }: RootPageProps) { const authConfigured = isAdminAuthConfigured(); const basicConfigured = Boolean( process.env.ROOT_BASIC_AUTH_USER && process.env.ROOT_BASIC_AUTH_PASS, ); const authenticated = isAdminAuthenticated(); const lockState = getAdminLockState(); async function loginAction(formData: FormData) { "use server"; const password = String(formData.get("password") ?? ""); const currentLockState = getAdminLockState(); if (currentLockState.locked) { redirect("/root?error=locked"); } if (!isAdminAuthConfigured() || !isPasswordValid(password)) { const failState = registerFailedAdminAttempt(); if (failState.locked) { redirect("/root?error=locked"); } redirect("/root?error=invalid"); } resetAdminFailedAttempts(); setAdminSessionCookie(); redirect("/root"); } async function logoutAction() { "use server"; clearAdminSessionCookie(); redirect("/root"); } if (!authenticated) { return (

{copy.loginTitle}

{copy.loginText}
{!authConfigured ? (

{copy.configMissing}

) : null} {!basicConfigured ? (

{copy.basicAuthMissing}

) : null} {searchParams?.error === "invalid" ? (

{copy.invalidLogin}

) : null} {searchParams?.error === "locked" || lockState.locked ? (

{copy.lockedLogin}

) : null}
); } const [maintenanceEnabled, categories, projects, mediaAssets] = await Promise.all([ getMaintenanceMode(), getAdminPortfolioCategories(), getAdminPortfolioProjects(), getAdminMediaAssets(), ]); const publishedProjects = projects.filter((project) => project.isPublished).length; const rootNavigation = getRootNavigation(copy, "overview"); const authHealthy = authConfigured && basicConfigured; const sectionRows: Array<{ label: string; summary: string; status: string; href: string; badgeVariant: BadgeProps["variant"]; action: string; }> = [ { label: copy.maintenanceTitle, summary: copy.maintenanceDescription, status: maintenanceEnabled ? copy.maintenanceStatusOn : copy.maintenanceStatusOff, href: "/root/maintenance", badgeVariant: maintenanceEnabled ? "warning" : "success", action: copy.maintenanceAction, }, { label: copy.mediaTitle, summary: `${mediaAssets.length} ${copy.totalMedia}`, status: copy.authStatusOk, href: "/root/media", badgeVariant: "outline" as const, action: copy.mediaAction, }, { label: copy.portfolioTitle, summary: `${projects.length} ${copy.totalProjects} / ${categories.length} ${copy.totalCategories}`, status: `${publishedProjects} ${copy.publishedProjects}`, href: "/root/portfolio", badgeVariant: "outline" as const, action: copy.portfolioAction, }, { label: copy.uiKitTitle, summary: copy.uiKitDescription, status: copy.authStatusOk, href: "/root/ui-kit", badgeVariant: "outline" as const, action: copy.uiKitAction, }, ]; return ( } headerActions={
{maintenanceEnabled ? ( ) : null}
} >
{copy.tableTitle} {copy.tableDescription} {copy.tableSection} {copy.tableSummary} {copy.tableStatus} {copy.tableAction} {sectionRows.map((row) => ( {row.label} {row.summary} {row.status} ))}
{copy.systemStatusTitle} {copy.systemStatusDescription}

{copy.authCard}

{authHealthy ? copy.authDescription : copy.authDescriptionWarning}

{authHealthy ? copy.authStatusOk : copy.authStatusWarning}
{copy.maintenanceLabel} {maintenanceEnabled ? copy.maintenanceStatusOn : copy.maintenanceStatusOff}
{copy.contentLabel} {`${publishedProjects} ${copy.publishedProjects} / ${projects.length} ${copy.totalProjects}`}

{copy.contentDescription}

); }