140 lines
3.6 KiB
TypeScript
140 lines
3.6 KiB
TypeScript
import { defineField, defineType } from 'sanity'
|
||
|
||
export const postType = defineType({
|
||
name: 'post',
|
||
title: 'Пост',
|
||
type: 'document',
|
||
icon: () => '📝',
|
||
fields: [
|
||
defineField({
|
||
name: 'title',
|
||
title: 'Заголовок',
|
||
type: 'string',
|
||
validation: (Rule) => Rule.required().min(5).max(200),
|
||
}),
|
||
defineField({
|
||
name: 'slug',
|
||
title: 'Slug',
|
||
type: 'slug',
|
||
options: {
|
||
source: 'title',
|
||
maxLength: 96,
|
||
},
|
||
validation: (Rule) => Rule.required(),
|
||
}),
|
||
defineField({
|
||
name: 'excerpt',
|
||
title: 'Краткое описание',
|
||
type: 'text',
|
||
rows: 3,
|
||
description: 'Краткое описание для карточки поста (до 200 символов)',
|
||
validation: (Rule) => Rule.max(200),
|
||
}),
|
||
defineField({
|
||
name: 'mainImage',
|
||
title: 'Главное изображение',
|
||
type: 'image',
|
||
options: {
|
||
hotspot: true,
|
||
},
|
||
fields: [
|
||
{
|
||
name: 'alt',
|
||
type: 'string',
|
||
title: 'Альтернативный текст',
|
||
description: 'Важно для SEO и доступности',
|
||
},
|
||
],
|
||
}),
|
||
defineField({
|
||
name: 'author',
|
||
title: 'Автор',
|
||
type: 'reference',
|
||
to: { type: 'author' },
|
||
validation: (Rule) => Rule.required(),
|
||
}),
|
||
defineField({
|
||
name: 'categories',
|
||
title: 'Категории',
|
||
type: 'array',
|
||
of: [{ type: 'reference', to: { type: 'category' } }],
|
||
}),
|
||
defineField({
|
||
name: 'publishedAt',
|
||
title: 'Дата публикации',
|
||
type: 'datetime',
|
||
initialValue: () => new Date().toISOString(),
|
||
}),
|
||
defineField({
|
||
name: 'body',
|
||
title: 'Содержимое',
|
||
type: 'blockContent',
|
||
}),
|
||
defineField({
|
||
name: 'seo',
|
||
title: 'SEO',
|
||
type: 'object',
|
||
options: {
|
||
collapsible: true,
|
||
collapsed: true,
|
||
},
|
||
fields: [
|
||
{
|
||
name: 'metaTitle',
|
||
title: 'Meta Title',
|
||
type: 'string',
|
||
description: 'Оставьте пустым для использования заголовка поста',
|
||
},
|
||
{
|
||
name: 'metaDescription',
|
||
title: 'Meta Description',
|
||
type: 'text',
|
||
rows: 3,
|
||
description: 'Оставьте пустым для использования краткого описания',
|
||
},
|
||
{
|
||
name: 'ogImage',
|
||
title: 'Open Graph изображение',
|
||
type: 'image',
|
||
description: 'Изображение для социальных сетей (1200x630)',
|
||
},
|
||
],
|
||
}),
|
||
],
|
||
preview: {
|
||
select: {
|
||
title: 'title',
|
||
author: 'author.name',
|
||
media: 'mainImage',
|
||
date: 'publishedAt',
|
||
},
|
||
prepare({ title, author, media, date }) {
|
||
const formattedDate = date
|
||
? new Date(date).toLocaleDateString('ru-RU')
|
||
: 'Не опубликовано'
|
||
return {
|
||
title,
|
||
subtitle: `${author || 'Без автора'} • ${formattedDate}`,
|
||
media,
|
||
}
|
||
},
|
||
},
|
||
orderings: [
|
||
{
|
||
title: 'Дата публикации (новые)',
|
||
name: 'publishedAtDesc',
|
||
by: [{ field: 'publishedAt', direction: 'desc' }],
|
||
},
|
||
{
|
||
title: 'Дата публикации (старые)',
|
||
name: 'publishedAtAsc',
|
||
by: [{ field: 'publishedAt', direction: 'asc' }],
|
||
},
|
||
{
|
||
title: 'Заголовок',
|
||
name: 'titleAsc',
|
||
by: [{ field: 'title', direction: 'asc' }],
|
||
},
|
||
],
|
||
})
|