Implement portfolio admin management

This commit is contained in:
MOH
2026-03-07 16:06:20 +01:00
parent f983ce2203
commit ea64373853
35 changed files with 5126 additions and 178 deletions
+119
View File
@@ -0,0 +1,119 @@
import { redirect } from "next/navigation";
import { PortfolioProjectForm } from "@/components/root/portfolio-project-form";
import { PortfolioSubnav } from "@/components/root/portfolio-subnav";
import { RootDashboardShell } from "@/components/root/root-dashboard-shell";
import { AppCard } from "@/components/ui/app-card";
import { Button } from "@/components/ui/button";
import { CardContent } from "@/components/ui/card";
import { clearAdminSessionCookie, isAdminAuthenticated } from "@/lib/admin-auth";
import { getMediaOptions } from "@/lib/media";
import {
getActivePortfolioCategories,
getAdminPortfolioProjectById,
} from "@/lib/portfolio";
import { deleteProjectAction, saveProjectAction } from "../../actions";
export const dynamic = "force-dynamic";
const copy = {
title: "Portfolio Projekt bearbeiten",
subtitle: "Projektstatus, Inhalte und Dateien anpassen.",
overview: "Uebersicht",
maintenance: "Wartungsmodus",
uiKit: "UI Kit",
media: "Media",
portfolio: "Portfolio",
logout: "Ausloggen",
backToSite: "Zur Website",
};
type RootPortfolioProjectPageProps = {
params: {
id: string;
};
searchParams?: {
success?: string;
error?: string;
};
};
export default async function RootPortfolioProjectPage({
params,
searchParams,
}: RootPortfolioProjectPageProps) {
if (!isAdminAuthenticated()) {
redirect("/root");
}
async function logoutAction() {
"use server";
clearAdminSessionCookie();
redirect("/root");
}
const [categories, mediaOptions, project] = await Promise.all([
getActivePortfolioCategories(),
getMediaOptions(),
getAdminPortfolioProjectById(params.id),
]);
if (!project) {
redirect("/root/portfolio/projects?error=Project+not+found.");
}
return (
<RootDashboardShell
copy={copy}
active="portfolio"
portfolioChild="projects"
logoutAction={logoutAction}
headerTitle={copy.title}
headerDescription={copy.subtitle}
>
<div className="space-y-6">
<PortfolioSubnav active="projects" />
{searchParams?.success ? (
<p className="rounded-nested border border-status-success/30 bg-status-success/10 px-4 py-3 text-sm text-status-success">
{searchParams.success}
</p>
) : null}
{searchParams?.error ? (
<p className="rounded-nested border border-destructive/30 bg-destructive/10 px-4 py-3 text-sm text-destructive">
{searchParams.error}
</p>
) : null}
<PortfolioProjectForm
action={saveProjectAction}
categories={categories}
mediaOptions={mediaOptions}
project={project}
redirectPath={`/root/portfolio/projects/${project.id}`}
submitLabel="Save Project"
/>
<AppCard>
<CardContent className="flex items-center justify-between gap-4 p-6">
<div>
<p className="text-sm font-medium text-foreground">Danger Zone</p>
<p className="mt-1 text-sm text-muted-foreground">
Project records are deleted from the database. Uploaded files stay on disk.
</p>
</div>
<form action={deleteProjectAction}>
<input type="hidden" name="id" value={project.id} />
<Button type="submit" variant="destructive">
Delete Project
</Button>
</form>
</CardContent>
</AppCard>
</div>
</RootDashboardShell>
);
}