shumiland-web/src/collections/Blog.ts
Vadym Samoilenko 61e73033fe feat: Phase 2 complete — content model, blocks, collections, globals
- 11 Page Builder blocks: HeroBlock, TextBlock, FeaturesBlock,
  LocationCardBlock, PricingBlock, GalleryBlock, FormBlock (with label
  relation for lead tracking), CTABlock, CountdownBlock, BlogPreviewBlock,
  MapBlock
- Collections: Labels, Pages, LandingPages, Blog, Events, Leads (UTM +
  googleClientId + label relation), Orders, Tickets
- Globals: SiteSettings (GTM/GA4/Binotel/Umami), TicketsConfig, Navigation
- Added "type": "module" to package.json for Payload CLI ESM compatibility
- payload-types.ts generated (1874 lines)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-02 17:52:54 +01:00

161 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { CollectionConfig } from 'payload'
import { isAdmin, isEditor, isAdminOrPublished } from '../access'
export const Blog: CollectionConfig = {
slug: 'blog',
labels: {
singular: 'Пост блогу',
plural: 'Пости блогу',
},
admin: {
group: 'Контент',
useAsTitle: 'title',
defaultColumns: ['title', 'category', 'publishedAt', '_status'],
preview: (doc) => {
if (doc?.slug) {
return `${process.env.NEXT_PUBLIC_SITE_URL}/blog/${doc.slug}`
}
return null
},
},
access: {
read: isAdminOrPublished,
create: isEditor,
update: isEditor,
delete: isAdmin,
},
versions: {
drafts: {
autosave: {
interval: 375,
},
},
},
fields: [
{
name: 'title',
type: 'text',
label: 'Заголовок',
required: true,
admin: {
placeholder: 'Як ми готуємось до літнього сезону',
},
},
{
name: 'slug',
type: 'text',
label: 'URL (slug)',
required: true,
unique: true,
index: true,
admin: {
placeholder: 'litniy-sezon-2026',
description: 'Автоматично генерується з заголовку. Доступний за: /blog/{slug}',
},
},
{
name: 'excerpt',
type: 'textarea',
label: 'Короткий опис (анонс)',
admin: {
rows: 3,
placeholder: 'Розповідаємо про нові атракціони та заходи цього сезону',
description: 'Відображається в картці на списку блогу та в META description',
},
},
{
name: 'coverImage',
type: 'upload',
relationTo: 'media',
label: 'Обкладинка',
admin: {
description: 'Рекомендований розмір: 1200×630 px',
},
},
{
name: 'category',
type: 'select',
label: 'Категорія',
options: [
{ label: 'Новини', value: 'news' },
{ label: 'Заходи', value: 'events' },
{ label: 'Корисне', value: 'tips' },
{ label: 'За лаштунками', value: 'behind-scenes' },
],
admin: {
placeholder: 'Оберіть категорію',
},
},
{
name: 'tags',
type: 'array',
label: 'Теги',
maxRows: 10,
fields: [
{
name: 'tag',
type: 'text',
label: 'Тег',
required: true,
admin: {
placeholder: 'динопарк',
},
},
],
},
{
name: 'publishedAt',
type: 'date',
label: 'Дата публікації',
admin: {
date: {
pickerAppearance: 'dayOnly',
displayFormat: 'd MMM yyyy',
},
description: 'Відображається на сторінці. Якщо не вказано — використовується дата створення.',
},
},
{
name: 'body',
type: 'richText',
label: 'Контент',
required: true,
admin: {
description: 'Повний текст статті',
},
},
{
name: 'seo',
type: 'group',
label: 'SEO',
fields: [
{
name: 'metaTitle',
type: 'text',
label: 'Meta Title',
admin: {
description: 'Якщо порожнє — використовується заголовок поста',
},
},
{
name: 'metaDescription',
type: 'textarea',
label: 'Meta Description',
admin: {
rows: 3,
description: 'Якщо порожнє — використовується анонс поста',
},
},
{
name: 'ogImage',
type: 'upload',
relationTo: 'media',
label: 'OG Image',
admin: {
description: 'Якщо порожнє — використовується обкладинка',
},
},
],
},
],
}