1
0
Fork 0
polaris-task-force/src/app/(frontend)/wiki/[slug]/edit/page.tsx
Z8MB1E 51f7c803e0 feat(wiki): add scoped template CSS and developer HTML templates
WikiTemplates gains two fields: an optional css code field injected on
pages that use the template (selectors auto-scoped to a hash-based
.ptf-tpl-* class so templates never collide with each other or the app
shell), and a developer-only html field rendered verbatim (sanitized)
in place of the markdown body when transcluded.

- templateCss.ts scopes/sanitizes CSS, incl. nested at-rules; pages
  and the editor live preview inject scoped styles and drop the Card
  chrome in favor of a full-width article
- templateHtml.ts sanitizes HTML and splits prepared markdown around
  expansion sentinels so raw HTML renders without rehype-raw
- templates.ts adds extractTemplateNames/extractPlaceholders/
  extractDirectiveNames; prepareMarkdown threads an htmlTemplates map
- markdown pipeline gains rehype-highlight for fenced code blocks
- styles.css adds wiki prose typography + infobox demo styling
- docs: demo article and Infobox template authoring guide
2026-09-06 05:32:48 -04:00

140 lines
4.3 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";
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 [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}
/>
</CardContent>
</Card>
</div>
);
}