New in-depth, non-developer guides for the core systems: logistics and storage rules, banking, market and negotiation, shipments, base management, and the map. Read stays open to any logged-in user; create and edit require admin or developer roles (enforced in the collection access functions and the wiki server actions, since the service layer runs with overrideAccess). Category escalation via update is blocked too. Ships the enum migration, regenerated schema/types, and the idempotent seed script with its markdown sources.
167 lines
5.4 KiB
TypeScript
167 lines
5.4 KiB
TypeScript
import config from "@payload-config";
|
|
import Link from "next/link";
|
|
import { redirect } from "next/navigation";
|
|
import { headers as nextHeaders } from "next/headers";
|
|
import { getPayload } from "payload";
|
|
import { ArrowLeftIcon, PencilIcon } from "lucide-react";
|
|
import { Card, CardContent } from "@/components/ui/card";
|
|
import { LockdownBanner } from "@/components/frontend/wiki/LockdownBanner";
|
|
import { WikiEditor } from "@/components/frontend/wiki/WikiEditor";
|
|
import { listTemplates, templateMap } from "@/lib/wiki/service";
|
|
import hasRoles from "@/utils/access-control/hasRoles";
|
|
|
|
export const metadata = {
|
|
title: "Edit wiki page: Polaris Task Force",
|
|
};
|
|
|
|
function templateCssMap(
|
|
templates: readonly { name: string; css?: string | null }[],
|
|
): Record<string, string> {
|
|
const map: Record<string, string> = {};
|
|
for (const template of templates) {
|
|
if (template.css && template.css.trim().length > 0) map[template.name] = template.css;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
function templateHtmlMap(
|
|
templates: readonly { name: string; html?: string | null }[],
|
|
): Record<string, string> {
|
|
const map: Record<string, string> = {};
|
|
for (const template of templates) {
|
|
if (template.html && template.html.trim().length > 0) map[template.name] = template.html;
|
|
}
|
|
return map;
|
|
}
|
|
|
|
export default async function EditWikiPage({
|
|
params,
|
|
}: {
|
|
readonly params: Promise<{ slug: string }>;
|
|
}) {
|
|
const { slug } = await params;
|
|
const payload = await getPayload({ config });
|
|
const headers = await nextHeaders();
|
|
const { user } = await payload.auth({ headers, canSetHeaders: false });
|
|
const pageResult = await payload.find({
|
|
collection: "wiki-pages",
|
|
where: { slug: { equals: slug } },
|
|
limit: 1,
|
|
depth: 1,
|
|
overrideAccess: true,
|
|
});
|
|
const page = pageResult.docs[0];
|
|
|
|
if (!page) {
|
|
redirect(`/wiki/new?title=${encodeURIComponent(slug)}`);
|
|
}
|
|
|
|
if (page.lockdown) {
|
|
return (
|
|
<div className="flex flex-col gap-6 p-5">
|
|
<Link
|
|
href={`/wiki/${page.slug}`}
|
|
className="flex w-fit items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-primary"
|
|
>
|
|
<ArrowLeftIcon className="size-3" />
|
|
Back to page
|
|
</Link>
|
|
<div className="flex items-center gap-2">
|
|
<PencilIcon className="size-5 text-muted-foreground" />
|
|
<h1 className="text-lg font-semibold">Edit {page.title}</h1>
|
|
</div>
|
|
<LockdownBanner />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const canEditDocumentation = hasRoles(["admin"], user);
|
|
if (page.category === "Documentation" && !canEditDocumentation) {
|
|
return (
|
|
<div className="flex flex-col gap-6 p-5">
|
|
<Link
|
|
href={`/wiki/${page.slug}`}
|
|
className="flex w-fit items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-primary"
|
|
>
|
|
<ArrowLeftIcon className="size-3" />
|
|
Back to page
|
|
</Link>
|
|
<div className="flex items-center gap-2">
|
|
<PencilIcon className="size-5 text-muted-foreground" />
|
|
<h1 className="text-lg font-semibold">Edit {page.title}</h1>
|
|
</div>
|
|
<Card>
|
|
<CardContent className="p-6 text-sm text-muted-foreground">
|
|
Documentation pages are maintained by admins and developers. If something here is out
|
|
of date, contact the staff team.
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
const [templates, pagesResult, hasEditedRevisions] = await Promise.all([
|
|
listTemplates(payload),
|
|
payload.find({
|
|
collection: "wiki-pages",
|
|
sort: "title",
|
|
limit: 200,
|
|
depth: 0,
|
|
overrideAccess: true,
|
|
}),
|
|
user
|
|
? payload
|
|
.find({
|
|
collection: "wiki-revisions",
|
|
where: { editor: { equals: user.id } },
|
|
limit: 1,
|
|
depth: 0,
|
|
overrideAccess: true,
|
|
})
|
|
.then((result) => result.docs.length > 0)
|
|
: Promise.resolve(true),
|
|
]);
|
|
const existingPages = pagesResult.docs.map((existingPage) => ({ title: existingPage.title }));
|
|
|
|
return (
|
|
<div className="flex flex-col gap-6 p-5">
|
|
<div className="flex flex-col gap-3">
|
|
<Link
|
|
href={`/wiki/${page.slug}`}
|
|
className="flex w-fit items-center gap-1 text-sm text-muted-foreground transition-colors hover:text-primary"
|
|
>
|
|
<ArrowLeftIcon className="size-3" />
|
|
Back to page
|
|
</Link>
|
|
<div className="flex items-center gap-2">
|
|
<PencilIcon className="size-5 text-muted-foreground" />
|
|
<h1 className="text-lg font-semibold">Edit {page.title}</h1>
|
|
</div>
|
|
<p className="text-sm text-muted-foreground">
|
|
Update the article and leave a short note for the revision history.
|
|
</p>
|
|
</div>
|
|
|
|
<Card>
|
|
<CardContent className="p-6">
|
|
<WikiEditor
|
|
mode="edit"
|
|
initial={{
|
|
id: page.id,
|
|
title: page.title,
|
|
category: page.category,
|
|
tags: page.tags ?? [],
|
|
body: page.body,
|
|
}}
|
|
templates={templateMap(templates)}
|
|
templateCssMap={templateCssMap(templates)}
|
|
templateHtmlMap={templateHtmlMap(templates)}
|
|
existingPages={existingPages}
|
|
isFirstEdit={!hasEditedRevisions}
|
|
canEditDocumentation={canEditDocumentation}
|
|
/>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
}
|