This commit is contained in:
1 parent
8efd032cc6
commit
df38dccbf1
735 files changed
+128321
-120870
No files matched your search
+105
-83
@@ -1,96 +1,118 @@
|
||||
import { Newspaper } from "lucide-react";
|
||||
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 { StatusCard } from "@/components/admin/dashboard";
|
||||
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(() => []);
|
||||
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) : "—";
|
||||
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(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
|
||||
<Newspaper size={20} className="text-[var(--admin-accent)]" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">{t("title")}</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-text-muted)] mt-0.5">{t("subtitle")}</p>
|
||||
</div>
|
||||
<Link href="/admin/articles/new" className="btn btn-primary">
|
||||
+ {t("newArticle")}
|
||||
</Link>
|
||||
</div>
|
||||
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(--admin-accent)]/20 to-[var(--admin-accent)]/5 grid place-items-center">
|
||||
<Newspaper size={20} className="text-[var(--admin-accent)]" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h1 className="m-0 text-xl font-extrabold text-[var(--admin-text)]">
|
||||
{t("title")}
|
||||
</h1>
|
||||
<p className="m-0 text-xs text-[var(--admin-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-[var(--admin-error)] dark:text-[var(--admin-error)] mb-4">
|
||||
Error: {error}
|
||||
</p>
|
||||
) : null}
|
||||
{error ? (
|
||||
<p className="text-xs text-[var(--admin-error)] dark:text-[var(--admin-error)] 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>
|
||||
<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 theme-text-muted dark:theme-text-muted whitespace-nowrap">
|
||||
<code>{a.slug}</code>
|
||||
</td>
|
||||
<td className="text-sm theme-text-muted dark:theme-text-muted 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>
|
||||
);
|
||||
<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 theme-text-muted dark:theme-text-muted whitespace-nowrap">
|
||||
<code>{a.slug}</code>
|
||||
</td>
|
||||
<td className="text-sm theme-text-muted dark:theme-text-muted 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>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user