Files
EpicNext-Cms/src/app/admin/articles/page.tsx
T
openhands 41be6835bf Add missing admin action files and navigation links
- Add 11 missing server action files: badges, bulk-users, catalog, catalog-bc, catalog-items, import-badges, import-furni, multi-account-detect, permissions, rooms, soundtracks
- Add missing admin navigation links: tickets, sounds, translations, import, radio sub-pages
- Add translation keys for all new navigation items
2026-07-11 12:01:05 +02:00

93 lines
3.6 KiB
TypeScript

import Link from "next/link";
import { getTranslations } from "next-intl/server";
import { Newspaper } from "lucide-react";
import { StatusCard } from "@/components/admin/dashboard";
import { deleteArticle } from "@/actions/admin-articles";
import { prisma } from "@/lib/prisma";
export const dynamic = "force-dynamic";
export default async function AdminArticles({ searchParams }: { searchParams: Promise<{ error?: string }> }) {
const { error } = await searchParams;
const t = await getTranslations("pages.admin.articles");
const articles = await prisma.websiteArticles
.findMany({
orderBy: { createdAt: "desc" },
select: { id: true, title: true, slug: true, createdAt: true },
take: 50,
})
.catch(() => []);
const latest = articles[0]?.createdAt ? articles[0].createdAt.toISOString().slice(0, 10) : "—";
return (
<main>
<div className="flex items-center gap-3 mb-6">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-[var(--color-primary)]/20 to-[var(--color-primary)]/5 grid place-items-center">
<Newspaper size={20} className="text-[var(--color-primary)]" />
</div>
<div className="flex-1">
<h1 className="m-0 text-xl font-extrabold text-[var(--color-text)]">{t("title")}</h1>
<p className="m-0 text-xs text-[var(--color-text-muted)] mt-0.5">{t("subtitle")}</p>
</div>
<Link href="/admin/articles/new" className="btn btn-primary">
+ {t("newArticle")}
</Link>
</div>
{error ? <p className="text-xs text-red-600 dark:text-red-400 mb-4">Error: {error}</p> : null}
<div className="grid grid-cols-[repeat(auto-fit,minmax(180px,1fr))] gap-3.5 mb-6">
<StatusCard label={t("articlesShown")} value={articles.length} icon="📰" />
<StatusCard label={t("latest")} value={latest} hint={t("mostRecentPost")} icon="🕒" />
</div>
<section className="mt-6">
<h2 className="admin-section-title">{t("recentArticles")}</h2>
{articles.length === 0 ? (
<div className="admin-empty">{t("noArticles")}</div>
) : (
<table>
<thead>
<tr>
<th>{t("colTitle")}</th>
<th>{t("colSlug")}</th>
<th>{t("colCreated")}</th>
<th>{t("colActions")}</th>
</tr>
</thead>
<tbody>
{articles.map((a) => (
<tr key={String(a.id)}>
<td>
<Link href={`/admin/articles/${a.id}`}>{a.title}</Link>
</td>
<td className="text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">
<code>{a.slug}</code>
</td>
<td className="text-sm text-gray-500 dark:text-gray-400 whitespace-nowrap">
{a.createdAt ? a.createdAt.toISOString().slice(0, 10) : "—"}
</td>
<td>
<div className="flex gap-2">
<Link href={`/admin/articles/${a.id}`} className="btn btn-primary">
{t("edit")}
</Link>
<form action={deleteArticle} className="m-0">
<input type="hidden" name="id" value={String(a.id)} />
<button type="submit" className="btn btn-danger">
{t("delete")}
</button>
</form>
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
</section>
</main>
);
}