feat(petition): add petition collection with live preview and public page

- create  collection with localized admin labels and rich text body
- enable drafts/autosave/scheduled publish and collection-level live preview URL
- register  in
- add frontend route  with static params and enabled check
- add  helper to fetch collection docs for static generation
- regenerate  and admin import map for new collection/editor imports
This commit is contained in:
Francisco Ignácio Fontoura Solla 2026-02-15 23:52:33 -03:00
parent f26e5d7537
commit 84c7c738c4
6 changed files with 199 additions and 1 deletions

View file

@ -0,0 +1,30 @@
import { getCachedDocumentById, getDocuments } from '@/utilities/documents'
import { convertLexicalToHTML } from '@payloadcms/richtext-lexical/html'
import { notFound } from 'next/navigation'
import { headers as nextHeaders } from 'next/headers'
export async function generateStaticParams() {
const payload = await getDocuments('petition')
return payload.docs.map((doc) => ({
id: doc.id,
}))
}
export default async function Page({ params }: { params: Promise<{ id: string }> }) {
const petition = await getCachedDocumentById('petition', (await params).id)()
if (!petition || !petition.enabled) {
return notFound()
}
return (
<div>
<h1>{petition.title}</h1>
<h2>{petition.subtitle}</h2>
<article
dangerouslySetInnerHTML={{ __html: convertLexicalToHTML({ data: petition.body }) }}
/>
</div>
)
}

View file

@ -1,5 +1,51 @@
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
export const importMap = {
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
}

View file

@ -0,0 +1,66 @@
import { revalidateDocumentById } from '@/utilities/documents'
import type { CollectionConfig } from 'payload'
const slug = 'petition'
export const Petition: CollectionConfig<typeof slug> = {
slug,
labels: {
singular: 'Abaixo-assinado',
plural: 'Abaixo-assinados',
},
admin: {
group: 'Coleções',
useAsTitle: 'title',
livePreview: {
url: ({ data }) => `/abaixo-assinado/${data.id}`,
},
},
versions: {
drafts: {
autosave: {
interval: 1000,
},
schedulePublish: true,
},
maxPerDoc: 5,
},
access: {
read: () => true,
},
hooks: {
beforeChange: [({ data }) => revalidateDocumentById(slug, data.id)],
},
fields: [
{
name: 'id',
type: 'text',
label: 'Trecho Identificador da URL',
required: true,
},
{
name: 'title',
type: 'text',
label: 'Título',
required: true,
},
{
name: 'subtitle',
type: 'text',
label: 'Subtítulo',
required: true,
},
{
name: 'enabled',
type: 'checkbox',
label: 'Ativo',
required: true,
},
{
name: 'body',
type: 'richText',
label: 'Corpo',
required: true,
},
],
}

View file

@ -69,6 +69,7 @@ export interface Config {
collections: {
users: User;
media: Media;
petition: Petition;
'payload-kv': PayloadKv;
'payload-jobs': PayloadJob;
'payload-locked-documents': PayloadLockedDocument;
@ -79,6 +80,7 @@ export interface Config {
collectionsSelect: {
users: UsersSelect<false> | UsersSelect<true>;
media: MediaSelect<false> | MediaSelect<true>;
petition: PetitionSelect<false> | PetitionSelect<true>;
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
'payload-jobs': PayloadJobsSelect<false> | PayloadJobsSelect<true>;
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
@ -177,6 +179,34 @@ export interface Media {
focalX?: number | null;
focalY?: number | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "petition".
*/
export interface Petition {
id: string;
title: string;
subtitle: string;
enabled: boolean;
body: {
root: {
type: string;
children: {
type: any;
version: number;
[k: string]: unknown;
}[];
direction: ('ltr' | 'rtl') | null;
format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | '';
indent: number;
version: number;
};
[k: string]: unknown;
};
updatedAt: string;
createdAt: string;
_status?: ('draft' | 'published') | null;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv".
@ -300,6 +330,10 @@ export interface PayloadLockedDocument {
| ({
relationTo: 'media';
value: number | Media;
} | null)
| ({
relationTo: 'petition';
value: string | Petition;
} | null);
globalSlug?: string | null;
user: {
@ -383,6 +417,20 @@ export interface MediaSelect<T extends boolean = true> {
focalX?: T;
focalY?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "petition_select".
*/
export interface PetitionSelect<T extends boolean = true> {
id?: T;
title?: T;
subtitle?: T;
enabled?: T;
body?: T;
updatedAt?: T;
createdAt?: T;
_status?: T;
}
/**
* This interface was referenced by `Config`'s JSON-Schema
* via the `definition` "payload-kv_select".
@ -581,6 +629,10 @@ export interface TaskSchedulePublish {
input: {
type?: ('publish' | 'unpublish') | null;
locale?: string | null;
doc?: {
relationTo: 'petition';
value: string | Petition;
} | null;
global?: 'site-settings' | null;
user?: (number | null) | User;
};

View file

@ -11,6 +11,7 @@ import { SiteSettings } from './globals/SiteSettings'
import { pt } from 'payload/i18n/pt'
import { HomePage } from './globals/HomePage'
import { Metadata } from './globals/Metadata'
import { Petition } from './collections/Petition'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
@ -26,7 +27,7 @@ export default buildConfig({
globals: [SiteSettings.slug, HomePage.slug],
},
},
collections: [Users, Media],
collections: [Users, Media, Petition],
globals: [SiteSettings, HomePage, Metadata],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET || '',

View file

@ -7,6 +7,9 @@ import { getPayload } from 'payload'
type Collection = keyof Config['collections']
export const getDocuments = <Slug extends Collection>(collection: Slug, depth?: number) =>
getPayload({ config: configPromise }).then((payload) => payload.find({ collection, depth }))
export const getDocumentById = <Slug extends Collection>(
collection: Slug,
id: string,