This commit is contained in:
@@ -37,9 +37,6 @@ export default defineConfig({
|
||||
.schemaType('theaterHomepage')
|
||||
.documentId('theaterHomepage')
|
||||
),
|
||||
S.divider(),
|
||||
S.documentTypeListItem('post').title('Blog Posts'),
|
||||
S.documentTypeListItem('page').title('Pages'),
|
||||
]),
|
||||
}),
|
||||
visionTool(),
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { sanityClient } from '../../sanity/client';
|
||||
import { renderPortableText } from '../../sanity/portableText';
|
||||
import { urlFor } from '../../sanity/imageUrl';
|
||||
import { allPostsQuery } from '../../sanity/queries';
|
||||
|
||||
export async function getStaticPaths() {
|
||||
const posts = await sanityClient.fetch(allPostsQuery);
|
||||
return posts.map((post: any) => ({
|
||||
params: { slug: post.slug },
|
||||
props: { post },
|
||||
}));
|
||||
}
|
||||
|
||||
const { post } = Astro.props as { post: any };
|
||||
const bodyHtml = renderPortableText(post.body);
|
||||
---
|
||||
|
||||
<BaseLayout title={post.title} description={post.excerpt}>
|
||||
<article class="blog-post">
|
||||
<header class="post-header">
|
||||
<div class="container">
|
||||
<h1>{post.title}</h1>
|
||||
<p class="post-meta">
|
||||
<time datetime={post.date}>
|
||||
{new Date(post.date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{post.coverImage && (
|
||||
<div class="post-cover">
|
||||
<img src={urlFor(post.coverImage)} alt={post.title} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="post-content">
|
||||
<div class="container">
|
||||
<div class="prose" set:html={bodyHtml} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="post-footer">
|
||||
<div class="container">
|
||||
<a href="/blog" class="back-link">← Back to all posts</a>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.post-header {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
color: white;
|
||||
padding: 4rem 0 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.post-header h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 1rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 1.1rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.post-cover {
|
||||
width: 100%;
|
||||
max-height: 500px;
|
||||
overflow: hidden;
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.post-cover img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.prose {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.8;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.prose :global(h1) {
|
||||
font-size: 2.5rem;
|
||||
margin-top: 2.5rem;
|
||||
margin-bottom: 1rem;
|
||||
color: #2c3e50;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.prose :global(h2) {
|
||||
font-size: 2rem;
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
color: #2c3e50;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.prose :global(h3) {
|
||||
font-size: 1.5rem;
|
||||
margin-top: 1.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.prose :global(p) {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.prose :global(ul),
|
||||
.prose :global(ol) {
|
||||
margin-bottom: 1.5rem;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
.prose :global(li) {
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.prose :global(a) {
|
||||
color: #8b0000;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.prose :global(a:hover) {
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
.prose :global(strong) {
|
||||
font-weight: 700;
|
||||
color: #2c3e50;
|
||||
}
|
||||
|
||||
.prose :global(blockquote) {
|
||||
border-left: 4px solid #8b0000;
|
||||
padding-left: 1.5rem;
|
||||
margin: 2rem 0;
|
||||
font-style: italic;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.prose :global(code) {
|
||||
background-color: #f8f9fa;
|
||||
padding: 0.2rem 0.4rem;
|
||||
border-radius: 3px;
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.prose :global(pre) {
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
padding: 1.5rem;
|
||||
border-radius: 6px;
|
||||
overflow-x: auto;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.prose :global(pre code) {
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.prose :global(img) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
border-radius: 6px;
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
.post-footer {
|
||||
padding: 2rem 0 3rem;
|
||||
border-top: 1px solid #e1e8ed;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
color: #8b0000;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.post-header h1 {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.prose {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.prose :global(h1) {
|
||||
font-size: 2rem;
|
||||
}
|
||||
|
||||
.prose :global(h2) {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.prose :global(h3) {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.post-cover {
|
||||
max-height: 300px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,180 +0,0 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { sanityClient } from '../../sanity/client';
|
||||
import { urlFor } from '../../sanity/imageUrl';
|
||||
import { allPostsQuery } from '../../sanity/queries';
|
||||
|
||||
const allPosts = await sanityClient.fetch(allPostsQuery);
|
||||
---
|
||||
|
||||
<BaseLayout title="Blog" description="Latest news and updates from Theater Ziefen">
|
||||
<div class="blog-header">
|
||||
<div class="container">
|
||||
<h1>Blog</h1>
|
||||
<p>Stay updated with the latest news and behind-the-scenes insights</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blog-content">
|
||||
<div class="container">
|
||||
{allPosts.length === 0 ? (
|
||||
<p class="no-posts">No blog posts yet. Check back soon!</p>
|
||||
) : (
|
||||
<div class="posts-list">
|
||||
{allPosts.map((post: any) => (
|
||||
<article class="post-item">
|
||||
{post.coverImage && (
|
||||
<div class="post-image">
|
||||
<img src={urlFor(post.coverImage)} alt={post.title} />
|
||||
</div>
|
||||
)}
|
||||
<div class="post-content">
|
||||
<h2>
|
||||
<a href={`/blog/${post.slug}`}>
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time datetime={post.date}>
|
||||
{new Date(post.date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
</p>
|
||||
<p class="post-excerpt">{post.excerpt}</p>
|
||||
<a href={`/blog/${post.slug}`} class="read-more">
|
||||
Read full post →
|
||||
</a>
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
<style>
|
||||
.blog-header {
|
||||
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
|
||||
color: white;
|
||||
padding: 4rem 0 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.blog-header h1 {
|
||||
font-size: 3rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.blog-header p {
|
||||
font-size: 1.25rem;
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
.blog-content {
|
||||
padding: 3rem 0;
|
||||
}
|
||||
|
||||
.no-posts {
|
||||
text-align: center;
|
||||
font-size: 1.25rem;
|
||||
color: #666;
|
||||
padding: 4rem 0;
|
||||
}
|
||||
|
||||
.posts-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3rem;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.post-item {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
}
|
||||
|
||||
.post-item:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.post-image {
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.post-image img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.post-content h2 {
|
||||
font-size: 2rem;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.post-content h2 a {
|
||||
color: #2c3e50;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.post-content h2 a:hover {
|
||||
color: #8b0000;
|
||||
}
|
||||
|
||||
.post-meta {
|
||||
color: #999;
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.post-excerpt {
|
||||
color: #666;
|
||||
line-height: 1.7;
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.read-more {
|
||||
color: #8b0000;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.read-more:hover {
|
||||
color: #c0392b;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.blog-header h1 {
|
||||
font-size: 2.5rem;
|
||||
}
|
||||
|
||||
.blog-header p {
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
.post-content h2 {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.post-image {
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,17 @@
|
||||
import { toHTML } from '@portabletext/to-html'
|
||||
import { urlFor } from './imageUrl'
|
||||
|
||||
export function renderPortableText(blocks: any[]): string {
|
||||
if (!blocks?.length) return ''
|
||||
return toHTML(blocks)
|
||||
return toHTML(blocks, {
|
||||
components: {
|
||||
types: {
|
||||
image: ({ value }) => {
|
||||
const url = urlFor(value)
|
||||
if (!url) return ''
|
||||
return `<img src="${url}" alt="${value.alt ?? ''}" />`
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,34 +1 @@
|
||||
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
|
||||
}
|
||||
`
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
import { postSchema } from './post'
|
||||
import { pageSchema } from './page'
|
||||
import { theaterHomepageSchema } from './theaterHomepage'
|
||||
|
||||
export const schemaTypes = [postSchema, pageSchema, theaterHomepageSchema]
|
||||
export const schemaTypes = [theaterHomepageSchema]
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
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(),
|
||||
}),
|
||||
],
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
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(),
|
||||
}),
|
||||
],
|
||||
})
|
||||
@@ -33,8 +33,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -46,8 +46,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({
|
||||
name: 'organizations',
|
||||
title: 'Organizations',
|
||||
@@ -76,8 +76,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -89,8 +89,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
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' }),
|
||||
@@ -133,8 +133,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
],
|
||||
}),
|
||||
|
||||
@@ -163,8 +163,8 @@ export const theaterHomepageSchema = defineType({
|
||||
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: 'content_de', title: 'Content (German)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'content_en', title: 'Content (English)', type: 'array', of: [{ type: 'block' }, defineArrayMember({ type: 'image', options: { hotspot: true } })] }),
|
||||
defineField({ name: 'ticket_url', title: 'Ticket Purchase URL', type: 'url' }),
|
||||
],
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user