82 lines
2.3 KiB
TypeScript
82 lines
2.3 KiB
TypeScript
import type { CollectionConfig } from "payload";
|
|
import { FixedToolbarFeature, lexicalEditor } from "@payloadcms/richtext-lexical";
|
|
import hasRoles from "@/utils/access-control/hasRoles";
|
|
|
|
function isStoryAuthor(user: Parameters<typeof hasRoles>[1]): boolean {
|
|
return hasRoles(["admin", "developer"], user);
|
|
}
|
|
|
|
export const StoryBeats: CollectionConfig = {
|
|
slug: "story-beats",
|
|
admin: {
|
|
group: "Story",
|
|
useAsTitle: "title",
|
|
defaultColumns: ["title", "status", "occurredAt", "order", "mission"],
|
|
listSearchableFields: ["title", "body"],
|
|
description:
|
|
"Chronicle story beats. Drafts are visible to staff only; published beats are readable by any logged-in user on /story.",
|
|
},
|
|
access: {
|
|
// Guests see nothing; admin/developer see everything; any other logged-in
|
|
// user sees only published beats.
|
|
read: ({ req }) => {
|
|
if (!req.user) return false;
|
|
if (isStoryAuthor(req.user)) return true;
|
|
return { status: { equals: "published" } };
|
|
},
|
|
create: ({ req }) => isStoryAuthor(req.user),
|
|
update: ({ req }) => isStoryAuthor(req.user),
|
|
delete: ({ req }) => isStoryAuthor(req.user),
|
|
},
|
|
fields: [
|
|
{
|
|
name: "title",
|
|
type: "text",
|
|
required: true,
|
|
},
|
|
{
|
|
name: "occurredAt",
|
|
type: "date",
|
|
admin: {
|
|
description: "In-story timestamp used to order the chronicle.",
|
|
},
|
|
},
|
|
{
|
|
name: "order",
|
|
type: "number",
|
|
admin: {
|
|
description: "Tie-breaker for beats that share the same timestamp.",
|
|
},
|
|
},
|
|
{
|
|
name: "status",
|
|
type: "select",
|
|
required: true,
|
|
defaultValue: "draft",
|
|
index: true,
|
|
options: [
|
|
{ label: "Draft", value: "draft" },
|
|
{ label: "Published", value: "published" },
|
|
],
|
|
},
|
|
{
|
|
name: "body",
|
|
type: "richText",
|
|
required: true,
|
|
editor: lexicalEditor({
|
|
features: ({ defaultFeatures }) => [...defaultFeatures, FixedToolbarFeature()],
|
|
}),
|
|
admin: {
|
|
description: "Rich text body of the beat, rendered on /story with the Lexical renderer.",
|
|
},
|
|
},
|
|
{
|
|
name: "mission",
|
|
type: "relationship",
|
|
relationTo: "missions",
|
|
admin: {
|
|
description: "Optional mission this beat is tied to. Beats may stand alone.",
|
|
},
|
|
},
|
|
],
|
|
};
|