rda-v3/studio/schemaTypes/postType.ts
Calum Muir fc644510af Add Sanity CMS integration
- Install @sanity/client + @portabletext/to-html
- studio/ — Sanity v3 project with post schema (title, slug, date, category,
  summary, cover image with hotspot, Portable Text body). Run with
  `cd studio && npm install && sanity dev` after setting SANITY_STUDIO_PROJECT_ID.
- src/lib/sanity.ts — typed client, GROQ queries, Portable Text → HTML conversion
- src/lib/news.ts — adds getArticles() / getArticle() async functions that prefer
  Sanity when SANITY_PROJECT_ID is set; fall back to local markdown otherwise
- events/index.astro, events/[slug].astro, events/page/[page].astro — switch to
  async getArticles()/getArticle(); [slug].astro drops getStaticPaths() in favour
  of SSR request-time fetch with redirect-on-not-found
- .env.example — documents SANITY_PROJECT_ID / SANITY_DATASET / SANITY_API_TOKEN

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 11:50:35 +00:00

116 lines
2.9 KiB
TypeScript

import { defineField, defineType } from 'sanity';
export const postType = defineType({
name: 'post',
title: 'Post',
type: 'document',
fields: [
defineField({
name: 'title',
title: 'Title',
type: 'string',
validation: (r) => r.required(),
}),
defineField({
name: 'slug',
title: 'Slug',
type: 'slug',
options: { source: 'title', maxLength: 96 },
validation: (r) => r.required(),
}),
defineField({
name: 'date',
title: 'Date',
type: 'date',
options: { dateFormat: 'YYYY-MM-DD' },
validation: (r) => r.required(),
}),
defineField({
name: 'category',
title: 'Category',
type: 'string',
options: {
list: [
{ title: 'News', value: 'News' },
{ title: 'Events', value: 'Events' },
{ title: 'Fundraising', value: 'Fundraising' },
],
},
initialValue: 'News',
validation: (r) => r.required(),
}),
defineField({
name: 'summary',
title: 'Summary',
type: 'text',
rows: 3,
description: 'One or two sentences shown in article cards and meta descriptions.',
validation: (r) => r.required().max(300),
}),
defineField({
name: 'image',
title: 'Cover image',
type: 'image',
options: { hotspot: true },
fields: [
defineField({
name: 'alt',
title: 'Alt text',
type: 'string',
}),
],
}),
defineField({
name: 'body',
title: 'Body',
type: 'array',
of: [
{
type: 'block',
styles: [
{ title: 'Normal', value: 'normal' },
{ title: 'Heading 2', value: 'h2' },
{ title: 'Heading 3', value: 'h3' },
{ title: 'Quote', value: 'blockquote' },
],
marks: {
decorators: [
{ title: 'Bold', value: 'strong' },
{ title: 'Italic', value: 'em' },
],
annotations: [
{
name: 'link',
type: 'object',
title: 'Link',
fields: [
{
name: 'href',
type: 'url',
title: 'URL',
validation: (r) =>
r.uri({ allowRelative: true, scheme: ['http', 'https', 'mailto', 'tel'] }),
},
{
name: 'blank',
type: 'boolean',
title: 'Open in new tab',
initialValue: false,
},
],
},
],
},
},
{ type: 'image', options: { hotspot: true } },
],
}),
],
preview: {
select: {
title: 'title',
subtitle: 'date',
media: 'image',
},
},
});