1
0
Fork 0
polaris-task-force/src/collections/story/StoryBeats.ts

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.",
},
},
],
};