teqo/src/collections/Post.ts
Francisco Ignácio Fontoura Solla cf6ae6c47c Pass 2 W2: correcoes de fronteira lib/utilities e client boundary
- helpers puros movidos utilities->lib: slug, phone, wordStartFilter, voteEstimate (9 inversoes lib->utilities zeradas)
- chaves de consent em src/lib/campaignConsentKeys.ts (client-safe); campaignInvite/campaignConsent re-exportam; onda0ConsentTexts referencia as constantes
- 'server-only' em 21 loaders Payload/cache (CLI segue com --conditions=react-server)
- ciclo de tipos supporterListFilters <-> supporterUi quebrado (SupporterListFilterInput estrutural; sqlFilters idem)
- votePledgeData dividido: votePledgeViews (puro, client-safe) + votePledgeData (loaders, server-only) - padrao contract-module
- predicados de role client-safe em src/lib/campaignRoles.ts; access/shared deriva deles (fonte unica); nav.ts para de importar o barrel campaignAccess (casts 'as CampaignUser' eliminados)
2026-07-25 14:38:41 -03:00

117 lines
2.6 KiB
TypeScript

import { payloadAdminOnly } from '@/utilities/campaignAccess'
import { revalidateDocumentById, revalidatePostsListing } from '@/utilities/documents'
import { slugify } from '@/lib/slug'
import type { CollectionConfig } from 'payload'
const slug = 'post'
export const Post: CollectionConfig<typeof slug> = {
slug,
labels: {
singular: 'Publicação',
plural: 'Publicações',
},
admin: {
group: 'Publicações',
useAsTitle: 'title',
},
versions: {
drafts: {
schedulePublish: true,
},
maxPerDoc: 5,
},
access: {
read: () => true,
readVersions: payloadAdminOnly,
create: payloadAdminOnly,
update: payloadAdminOnly,
delete: payloadAdminOnly,
},
hooks: {
afterChange: [
({ doc, previousDoc, operation }) => {
// Admin create view creates the initial draft during render.
// Skip cache invalidation only for that first draft creation.
if (operation === 'create' && doc._status !== 'published') return
revalidateDocumentById(slug, previousDoc?.id ?? doc.id)
revalidatePostsListing()
},
],
},
fields: [
{
name: 'title',
type: 'text',
label: 'Título',
required: true,
},
{
name: 'slug',
type: 'text',
label: 'Slug',
unique: true,
index: true,
admin: {
description: 'Gerado automaticamente a partir do título quando vazio.',
},
hooks: {
beforeValidate: [
({ value, siblingData }) => {
if (value) return slugify(value as string)
if (siblingData?.title) return slugify(siblingData.title as string)
return value
},
],
},
},
{
name: 'type',
type: 'select',
label: 'Tipo',
required: true,
options: [
{ label: 'Notícia', value: 'noticia' },
{ label: 'Campanha', value: 'campanha' },
{ label: 'Artigo', value: 'artigo' },
{ label: 'Evento', value: 'evento' },
],
},
{
name: 'category',
type: 'relationship',
label: 'Categoria',
relationTo: 'tag',
required: true,
},
{
name: 'tags',
type: 'relationship',
label: 'Tags',
relationTo: 'tag',
hasMany: true,
},
{
name: 'subtitle',
type: 'text',
label: 'Subtítulo',
},
{
name: 'coverImage',
type: 'upload',
label: 'Imagem de capa',
relationTo: 'media',
},
{
name: 'publishedDate',
type: 'date',
label: 'Data de publicação',
},
{
name: 'body',
type: 'richText',
label: 'Corpo',
},
],
}