This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
# Sanity CMS Configuration
|
||||||
|
# Get your project ID from https://sanity.io/manage
|
||||||
|
PUBLIC_SANITY_PROJECT_ID=p49jotqu
|
||||||
|
PUBLIC_SANITY_DATASET=production
|
||||||
|
|
||||||
|
# Only needed when running the migration script (scripts/migrate-to-sanity.mjs)
|
||||||
|
# Create a write token at https://sanity.io/manage → API → Tokens
|
||||||
|
# Revoke after migration is complete.
|
||||||
|
SANITY_API_TOKEN=skQ3tzSF8tp5ETeDq26zlgYAoxv50L4D37uITONtQ0efe4wHxjH64NrauljoyucMGy0F58fOXmUbQXh7C0glLFcD0PUd0tVRwzifuuLYvlZmX0MKOo6aZB2U2P5ISydRNzKd0sPGyMjxRo47psGb3wCtzT0Xcv8ByMVOlqnn35bc6uDj7Afl
|
||||||
@@ -39,16 +39,6 @@ jobs:
|
|||||||
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${{ steps.tag.outputs.sha }}
|
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${{ steps.tag.outputs.sha }}
|
||||||
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:latest
|
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:latest
|
||||||
|
|
||||||
- name: Build and push backend image
|
|
||||||
uses: docker/build-push-action@v5
|
|
||||||
with:
|
|
||||||
context: .
|
|
||||||
target: backend
|
|
||||||
push: true
|
|
||||||
tags: |
|
|
||||||
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}-tina:${{ steps.tag.outputs.sha }}
|
|
||||||
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}-tina:latest
|
|
||||||
|
|
||||||
- name: Update image tags in gitops repo
|
- name: Update image tags in gitops repo
|
||||||
env:
|
env:
|
||||||
CI_TOKEN: ${{ secrets.CI_TOKEN }}
|
CI_TOKEN: ${{ secrets.CI_TOKEN }}
|
||||||
@@ -63,10 +53,7 @@ jobs:
|
|||||||
sed -i \
|
sed -i \
|
||||||
"s|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:.*|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${SHA}|" \
|
"s|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:.*|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${SHA}|" \
|
||||||
"apps/${{ env.APP }}/deployment.yaml"
|
"apps/${{ env.APP }}/deployment.yaml"
|
||||||
sed -i \
|
git add "apps/${{ env.APP }}/deployment.yaml"
|
||||||
"s|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}-tina:.*|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}-tina:${SHA}|" \
|
|
||||||
"apps/${{ env.APP }}/tina-backend-deployment.yaml"
|
|
||||||
git add "apps/${{ env.APP }}/deployment.yaml" "apps/${{ env.APP }}/tina-backend-deployment.yaml"
|
|
||||||
git diff --cached --quiet && echo "No changes, skipping commit." && exit 0
|
git diff --cached --quiet && echo "No changes, skipping commit." && exit 0
|
||||||
git commit -m "ci: update ${{ env.APP }} to ${SHA}"
|
git commit -m "ci: update ${{ env.APP }} to ${SHA}"
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|||||||
@@ -0,0 +1,30 @@
|
|||||||
|
import { defineConfig } from 'sanity'
|
||||||
|
import { structureTool } from 'sanity/structure'
|
||||||
|
import { visionTool } from '@sanity/vision'
|
||||||
|
import { schemaTypes } from './src/sanity/schemas'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
projectId: import.meta.env.PUBLIC_SANITY_PROJECT_ID,
|
||||||
|
dataset: import.meta.env.PUBLIC_SANITY_DATASET ?? 'production',
|
||||||
|
plugins: [
|
||||||
|
structureTool({
|
||||||
|
structure: (S) =>
|
||||||
|
S.list()
|
||||||
|
.title('Content')
|
||||||
|
.items([
|
||||||
|
S.listItem()
|
||||||
|
.title('Theater Homepage')
|
||||||
|
.child(
|
||||||
|
S.document()
|
||||||
|
.schemaType('theaterHomepage')
|
||||||
|
.documentId('theaterHomepage')
|
||||||
|
),
|
||||||
|
S.divider(),
|
||||||
|
S.documentTypeListItem('post').title('Blog Posts'),
|
||||||
|
S.documentTypeListItem('page').title('Pages'),
|
||||||
|
]),
|
||||||
|
}),
|
||||||
|
visionTool(),
|
||||||
|
],
|
||||||
|
schema: { types: schemaTypes },
|
||||||
|
})
|
||||||
@@ -0,0 +1,257 @@
|
|||||||
|
/**
|
||||||
|
* One-shot migration script: TinaCMS MDX files → Sanity documents
|
||||||
|
*
|
||||||
|
* Prerequisites:
|
||||||
|
* npm install --save-dev gray-matter @portabletext/markdown
|
||||||
|
*
|
||||||
|
* Usage:
|
||||||
|
* npm run migrate
|
||||||
|
*
|
||||||
|
* Revoke SANITY_API_TOKEN after migration is complete.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { createClient } from '@sanity/client'
|
||||||
|
import { createReadStream, readdirSync, readFileSync, existsSync } from 'fs'
|
||||||
|
import { join, basename } from 'path'
|
||||||
|
import matter from 'gray-matter'
|
||||||
|
import { markdownToPortableText } from '@portabletext/markdown'
|
||||||
|
|
||||||
|
const PROJECT_ID = process.env.PUBLIC_SANITY_PROJECT_ID
|
||||||
|
const TOKEN = process.env.SANITY_API_TOKEN
|
||||||
|
const DATASET = process.env.PUBLIC_SANITY_DATASET ?? 'production'
|
||||||
|
|
||||||
|
if (!PROJECT_ID || !TOKEN) {
|
||||||
|
console.error('Missing PUBLIC_SANITY_PROJECT_ID or SANITY_API_TOKEN environment variables.')
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const client = createClient({
|
||||||
|
projectId: PROJECT_ID,
|
||||||
|
dataset: DATASET,
|
||||||
|
apiVersion: '2024-01-01',
|
||||||
|
token: TOKEN,
|
||||||
|
useCdn: false,
|
||||||
|
})
|
||||||
|
|
||||||
|
function randomKey() {
|
||||||
|
return Math.random().toString(36).slice(2, 10)
|
||||||
|
}
|
||||||
|
|
||||||
|
function emptyPortableText() {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
function stringToPortableText(text) {
|
||||||
|
if (!text || text === '') return emptyPortableText()
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
_type: 'block',
|
||||||
|
_key: randomKey(),
|
||||||
|
style: 'normal',
|
||||||
|
markDefs: [],
|
||||||
|
children: [{ _type: 'span', _key: randomKey(), text, marks: [] }],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload a local image file to Sanity and return a Sanity image reference
|
||||||
|
async function uploadImageFile(localPath, label) {
|
||||||
|
if (!existsSync(localPath)) {
|
||||||
|
console.warn(` Skipping missing image: ${localPath}`)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
console.log(` Uploading image: ${label}`)
|
||||||
|
const asset = await client.assets.upload('image', createReadStream(localPath), {
|
||||||
|
filename: basename(localPath),
|
||||||
|
})
|
||||||
|
return { _type: 'image', asset: { _type: 'reference', _ref: asset._id } }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Map /images/filename.ext → public/images/filename.ext (relative to project root)
|
||||||
|
function localPathForImageUrl(imageUrl) {
|
||||||
|
if (!imageUrl || !imageUrl.startsWith('/images/')) return null
|
||||||
|
return join('public', imageUrl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Migrate theaterHomepage singleton
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
async function migrateTheaterHomepage() {
|
||||||
|
console.log('\n--- Migrating theaterHomepage ---')
|
||||||
|
const raw = readFileSync('content/theater/homepage.mdx', 'utf-8')
|
||||||
|
const { data: fm } = matter(raw)
|
||||||
|
|
||||||
|
// Pre-upload all organization logos
|
||||||
|
const orgs = (fm.team?.organizations ?? [])
|
||||||
|
const uploadedLogos = {}
|
||||||
|
for (const org of orgs) {
|
||||||
|
if (org.logo && !uploadedLogos[org.logo]) {
|
||||||
|
const localPath = localPathForImageUrl(org.logo)
|
||||||
|
if (localPath) {
|
||||||
|
uploadedLogos[org.logo] = await uploadImageFile(localPath, org.logo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = {
|
||||||
|
_type: 'theaterHomepage',
|
||||||
|
_id: 'theaterHomepage',
|
||||||
|
title: fm.title,
|
||||||
|
hero: fm.hero ?? {},
|
||||||
|
projekt: {
|
||||||
|
title_de: fm.projekt?.title_de ?? '',
|
||||||
|
title_en: fm.projekt?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.projekt?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.projekt?.content_en),
|
||||||
|
},
|
||||||
|
team: {
|
||||||
|
title_de: fm.team?.title_de ?? '',
|
||||||
|
title_en: fm.team?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.team?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.team?.content_en),
|
||||||
|
organizations: orgs.map((org) => ({
|
||||||
|
_key: randomKey(),
|
||||||
|
name: org.name,
|
||||||
|
role_de: org.role_de,
|
||||||
|
role_en: org.role_en,
|
||||||
|
url: org.url,
|
||||||
|
...(org.logo && uploadedLogos[org.logo] ? { logo: uploadedLogos[org.logo] } : {}),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
stueck: {
|
||||||
|
title_de: fm.stueck?.title_de ?? '',
|
||||||
|
title_en: fm.stueck?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.stueck?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.stueck?.content_en),
|
||||||
|
},
|
||||||
|
mitwirkung: {
|
||||||
|
title_de: fm.mitwirkung?.title_de ?? '',
|
||||||
|
title_en: fm.mitwirkung?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.mitwirkung?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.mitwirkung?.content_en),
|
||||||
|
director_name: fm.mitwirkung?.director_name,
|
||||||
|
director_email: fm.mitwirkung?.director_email,
|
||||||
|
director_phone: fm.mitwirkung?.director_phone,
|
||||||
|
},
|
||||||
|
termine: {
|
||||||
|
title_de: fm.termine?.title_de ?? '',
|
||||||
|
title_en: fm.termine?.title_en ?? '',
|
||||||
|
events: (fm.termine?.events ?? []).map((e) => ({ ...e, _key: randomKey() })),
|
||||||
|
},
|
||||||
|
presse: {
|
||||||
|
title_de: fm.presse?.title_de ?? '',
|
||||||
|
title_en: fm.presse?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.presse?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.presse?.content_en),
|
||||||
|
},
|
||||||
|
gallery: {
|
||||||
|
title_de: fm.gallery?.title_de ?? '',
|
||||||
|
title_en: fm.gallery?.title_en ?? '',
|
||||||
|
images: [],
|
||||||
|
},
|
||||||
|
tickets: {
|
||||||
|
title_de: fm.tickets?.title_de ?? '',
|
||||||
|
title_en: fm.tickets?.title_en ?? '',
|
||||||
|
content_de: stringToPortableText(fm.tickets?.content_de),
|
||||||
|
content_en: stringToPortableText(fm.tickets?.content_en),
|
||||||
|
...(fm.tickets?.ticket_url ? { ticket_url: fm.tickets.ticket_url } : {}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.createOrReplace(doc)
|
||||||
|
console.log(' ✓ theaterHomepage created/replaced')
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Migrate blog posts
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
async function migratePosts() {
|
||||||
|
console.log('\n--- Migrating blog posts ---')
|
||||||
|
const blogDir = 'content/blog'
|
||||||
|
if (!existsSync(blogDir)) {
|
||||||
|
console.log(' No blog directory found, skipping.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const files = readdirSync(blogDir).filter((f) => f.endsWith('.mdx'))
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const slug = file.replace('.mdx', '')
|
||||||
|
const raw = readFileSync(join(blogDir, file), 'utf-8')
|
||||||
|
const { data: fm, content: body } = matter(raw)
|
||||||
|
|
||||||
|
const portableTextBody = body.trim()
|
||||||
|
? markdownToPortableText(body)
|
||||||
|
: []
|
||||||
|
|
||||||
|
// Upload cover image if present and exists locally
|
||||||
|
let coverImage
|
||||||
|
if (fm.coverImage) {
|
||||||
|
const localPath = localPathForImageUrl(fm.coverImage)
|
||||||
|
if (localPath) {
|
||||||
|
coverImage = await uploadImageFile(localPath, fm.coverImage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const doc = {
|
||||||
|
_type: 'post',
|
||||||
|
_id: `post-${slug}`,
|
||||||
|
title: fm.title,
|
||||||
|
slug: { _type: 'slug', current: slug },
|
||||||
|
date: fm.date,
|
||||||
|
excerpt: fm.excerpt,
|
||||||
|
body: portableTextBody,
|
||||||
|
...(coverImage ? { coverImage } : {}),
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.createOrReplace(doc)
|
||||||
|
console.log(` ✓ Post migrated: ${slug}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Migrate static pages
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
async function migratePages() {
|
||||||
|
console.log('\n--- Migrating pages ---')
|
||||||
|
const pagesDir = 'content/pages'
|
||||||
|
if (!existsSync(pagesDir)) {
|
||||||
|
console.log(' No pages directory found, skipping.')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const files = readdirSync(pagesDir).filter((f) => f.endsWith('.mdx'))
|
||||||
|
|
||||||
|
for (const file of files) {
|
||||||
|
const slug = file.replace('.mdx', '')
|
||||||
|
const raw = readFileSync(join(pagesDir, file), 'utf-8')
|
||||||
|
const { data: fm, content: body } = matter(raw)
|
||||||
|
|
||||||
|
const portableTextBody = body.trim()
|
||||||
|
? markdownToPortableText(body)
|
||||||
|
: []
|
||||||
|
|
||||||
|
const doc = {
|
||||||
|
_type: 'page',
|
||||||
|
_id: `page-${slug}`,
|
||||||
|
title: fm.title,
|
||||||
|
slug: { _type: 'slug', current: slug },
|
||||||
|
description: fm.description,
|
||||||
|
body: portableTextBody,
|
||||||
|
}
|
||||||
|
|
||||||
|
await client.createOrReplace(doc)
|
||||||
|
console.log(` ✓ Page migrated: ${slug}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Run
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
try {
|
||||||
|
await migrateTheaterHomepage()
|
||||||
|
await migratePosts()
|
||||||
|
await migratePages()
|
||||||
|
console.log('\n✓ Migration complete.')
|
||||||
|
} catch (err) {
|
||||||
|
console.error('\n✗ Migration failed:', err)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
Vendored
+2
@@ -0,0 +1,2 @@
|
|||||||
|
/// <reference types="astro/client" />
|
||||||
|
/// <reference types="@sanity/astro/module" />
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { sanityClient } from 'sanity:client'
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { createImageUrlBuilder } from '@sanity/image-url'
|
||||||
|
|
||||||
|
const builder = createImageUrlBuilder({
|
||||||
|
projectId: import.meta.env.PUBLIC_SANITY_PROJECT_ID,
|
||||||
|
dataset: import.meta.env.PUBLIC_SANITY_DATASET ?? 'production',
|
||||||
|
})
|
||||||
|
|
||||||
|
export function urlFor(source: any): string {
|
||||||
|
if (!source) return ''
|
||||||
|
return builder.image(source).url()
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { toHTML } from '@portabletext/to-html'
|
||||||
|
|
||||||
|
export function renderPortableText(blocks: any[]): string {
|
||||||
|
if (!blocks?.length) return ''
|
||||||
|
return toHTML(blocks)
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
export const theaterHomepageQuery = `*[_type == "theaterHomepage" && _id == "theaterHomepage"][0]`
|
||||||
|
|
||||||
|
export const allPostsQuery = `
|
||||||
|
*[_type == "post"] | order(date desc) {
|
||||||
|
_id,
|
||||||
|
title,
|
||||||
|
"slug": slug.current,
|
||||||
|
date,
|
||||||
|
excerpt,
|
||||||
|
coverImage,
|
||||||
|
body
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const postBySlugQuery = `
|
||||||
|
*[_type == "post" && slug.current == $slug][0] {
|
||||||
|
_id,
|
||||||
|
title,
|
||||||
|
"slug": slug.current,
|
||||||
|
date,
|
||||||
|
excerpt,
|
||||||
|
coverImage,
|
||||||
|
body
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
export const pageBySlugQuery = `
|
||||||
|
*[_type == "page" && slug.current == $slug][0] {
|
||||||
|
_id,
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
body
|
||||||
|
}
|
||||||
|
`
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { postSchema } from './post'
|
||||||
|
import { pageSchema } from './page'
|
||||||
|
import { theaterHomepageSchema } from './theaterHomepage'
|
||||||
|
|
||||||
|
export const schemaTypes = [postSchema, pageSchema, theaterHomepageSchema]
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { defineType, defineField } from 'sanity'
|
||||||
|
|
||||||
|
export const pageSchema = defineType({
|
||||||
|
name: 'page',
|
||||||
|
title: 'Pages',
|
||||||
|
type: 'document',
|
||||||
|
fields: [
|
||||||
|
defineField({
|
||||||
|
name: 'title',
|
||||||
|
title: 'Title',
|
||||||
|
type: 'string',
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'slug',
|
||||||
|
title: 'Slug',
|
||||||
|
type: 'slug',
|
||||||
|
options: { source: 'title' },
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'description',
|
||||||
|
title: 'Description',
|
||||||
|
type: 'string',
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'body',
|
||||||
|
title: 'Content',
|
||||||
|
type: 'array',
|
||||||
|
of: [{ type: 'block' }],
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
import { defineType, defineField } from 'sanity'
|
||||||
|
|
||||||
|
export const postSchema = defineType({
|
||||||
|
name: 'post',
|
||||||
|
title: 'Blog Posts',
|
||||||
|
type: 'document',
|
||||||
|
fields: [
|
||||||
|
defineField({
|
||||||
|
name: 'title',
|
||||||
|
title: 'Title',
|
||||||
|
type: 'string',
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'slug',
|
||||||
|
title: 'Slug',
|
||||||
|
type: 'slug',
|
||||||
|
options: { source: 'title' },
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'date',
|
||||||
|
title: 'Publication Date',
|
||||||
|
type: 'datetime',
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'excerpt',
|
||||||
|
title: 'Excerpt',
|
||||||
|
type: 'string',
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'coverImage',
|
||||||
|
title: 'Cover Image',
|
||||||
|
type: 'image',
|
||||||
|
options: { hotspot: true },
|
||||||
|
}),
|
||||||
|
defineField({
|
||||||
|
name: 'body',
|
||||||
|
title: 'Body',
|
||||||
|
type: 'array',
|
||||||
|
of: [{ type: 'block' }, { type: 'image' }],
|
||||||
|
validation: (r) => r.required(),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
import { defineType, defineField, defineArrayMember } from 'sanity'
|
||||||
|
|
||||||
|
export const theaterHomepageSchema = defineType({
|
||||||
|
name: 'theaterHomepage',
|
||||||
|
title: 'Theater Homepage',
|
||||||
|
type: 'document',
|
||||||
|
// Singleton: disable create/delete — only update and publish
|
||||||
|
__experimental_actions: ['update', 'publish'],
|
||||||
|
fields: [
|
||||||
|
defineField({
|
||||||
|
name: 'title',
|
||||||
|
title: 'Page Title',
|
||||||
|
type: 'string',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Hero Section
|
||||||
|
defineField({
|
||||||
|
name: 'hero',
|
||||||
|
title: 'Hero Section',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'subtitle_de', title: 'Subtitle (German)', type: 'text' }),
|
||||||
|
defineField({ name: 'subtitle_en', title: 'Subtitle (English)', type: 'text' }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 1: Das Projekt / The Project
|
||||||
|
defineField({
|
||||||
|
name: 'projekt',
|
||||||
|
title: 'Section 1: Das Projekt / The Project',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 2: Wer steckt dahinter / Who's Behind It
|
||||||
|
defineField({
|
||||||
|
name: 'team',
|
||||||
|
title: "Section 2: Wer steckt dahinter / Who's Behind It",
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({
|
||||||
|
name: 'organizations',
|
||||||
|
title: 'Organizations',
|
||||||
|
type: 'array',
|
||||||
|
of: [
|
||||||
|
defineArrayMember({
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'name', title: 'Name', type: 'string' }),
|
||||||
|
defineField({ name: 'role_de', title: 'Role (German)', type: 'string' }),
|
||||||
|
defineField({ name: 'role_en', title: 'Role (English)', type: 'string' }),
|
||||||
|
defineField({ name: 'logo', title: 'Logo', type: 'image', options: { hotspot: false } }),
|
||||||
|
defineField({ name: 'url', title: 'Website URL', type: 'url' }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 3: Das Stück / The Play
|
||||||
|
defineField({
|
||||||
|
name: 'stueck',
|
||||||
|
title: 'Section 3: Das Stück / The Play',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 4: Mitwirkung / Participation
|
||||||
|
defineField({
|
||||||
|
name: 'mitwirkung',
|
||||||
|
title: 'Section 4: Mitwirkung / Participation',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'director_name', title: 'Director Name', type: 'string' }),
|
||||||
|
defineField({ name: 'director_email', title: 'Director Email', type: 'string' }),
|
||||||
|
defineField({ name: 'director_phone', title: 'Director Phone', type: 'string' }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 5: Termine / Schedule
|
||||||
|
defineField({
|
||||||
|
name: 'termine',
|
||||||
|
title: 'Section 5: Termine / Schedule',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({
|
||||||
|
name: 'events',
|
||||||
|
title: 'Events',
|
||||||
|
type: 'array',
|
||||||
|
of: [
|
||||||
|
defineArrayMember({
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Event Title (German)', type: 'string' }),
|
||||||
|
defineField({ name: 'title_en', title: 'Event Title (English)', type: 'string' }),
|
||||||
|
defineField({ name: 'date', title: 'Date / Period', type: 'string' }),
|
||||||
|
defineField({ name: 'description_de', title: 'Description (German)', type: 'text' }),
|
||||||
|
defineField({ name: 'description_en', title: 'Description (English)', type: 'text' }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 6: Presse / Press
|
||||||
|
defineField({
|
||||||
|
name: 'presse',
|
||||||
|
title: 'Section 6: Presse / Press',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 7: Foto Galerie / Photo Gallery
|
||||||
|
defineField({
|
||||||
|
name: 'gallery',
|
||||||
|
title: 'Section 7: Foto Galerie / Photo Gallery',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({
|
||||||
|
name: 'images',
|
||||||
|
title: 'Gallery Images',
|
||||||
|
type: 'array',
|
||||||
|
of: [{ type: 'image' }],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
|
||||||
|
// Section 8: Tickets
|
||||||
|
defineField({
|
||||||
|
name: 'tickets',
|
||||||
|
title: 'Section 8: Tickets',
|
||||||
|
type: 'object',
|
||||||
|
fields: [
|
||||||
|
defineField({ name: 'title_de', title: 'Title (German)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'title_en', title: 'Title (English)', type: 'string', validation: (r) => r.required() }),
|
||||||
|
defineField({ name: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }] }),
|
||||||
|
defineField({ name: 'ticket_url', title: 'Ticket Purchase URL', type: 'url' }),
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user