Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 2a0f7d5bcf | |||
| bf04f8679d |
@@ -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
|
||||
@@ -1,7 +0,0 @@
|
||||
# TinaCMS Configuration
|
||||
# For local development with Git-based workflow, these can be left empty
|
||||
# For TinaCMS Cloud, get these values from https://app.tina.io
|
||||
|
||||
TINA_CLIENT_ID=
|
||||
TINA_TOKEN=
|
||||
TINA_BRANCH=main
|
||||
@@ -39,16 +39,6 @@ jobs:
|
||||
${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${{ steps.tag.outputs.sha }}
|
||||
${{ 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
|
||||
env:
|
||||
CI_TOKEN: ${{ secrets.CI_TOKEN }}
|
||||
@@ -63,10 +53,7 @@ jobs:
|
||||
sed -i \
|
||||
"s|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:.*|image: ${{ env.REGISTRY }}/${{ env.ORG }}/${{ env.APP }}:${SHA}|" \
|
||||
"apps/${{ env.APP }}/deployment.yaml"
|
||||
sed -i \
|
||||
"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 add "apps/${{ env.APP }}/deployment.yaml"
|
||||
git diff --cached --quiet && echo "No changes, skipping commit." && exit 0
|
||||
git commit -m "ci: update ${{ env.APP }} to ${SHA}"
|
||||
git push origin main
|
||||
|
||||
@@ -16,7 +16,6 @@ pnpm-debug.log*
|
||||
|
||||
|
||||
# environment variables
|
||||
.env
|
||||
.env.production
|
||||
|
||||
# macOS-specific files
|
||||
|
||||
+4
-16
@@ -1,26 +1,14 @@
|
||||
# Shared build: generates /dist (Astro site + admin SPA) and tina/__generated__
|
||||
# Build: generates /dist (Astro static site + Sanity Studio SPA)
|
||||
FROM node:24 AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci --legacy-peer-deps
|
||||
RUN npm ci
|
||||
COPY . .
|
||||
ENV TINA_PUBLIC_IS_LOCAL=true
|
||||
RUN npm run build:docker
|
||||
RUN npm run build
|
||||
|
||||
# Frontend: Nginx serving the static site + TinaCMS admin SPA
|
||||
# Frontend: Nginx serving the static site + Sanity Studio SPA
|
||||
FROM nginx:alpine AS frontend
|
||||
RUN rm /etc/nginx/conf.d/default.conf
|
||||
COPY nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
|
||||
# Backend: TinaCMS self-hosted API server
|
||||
FROM node:24 AS backend
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm ci --legacy-peer-deps --omit=dev
|
||||
COPY backend/ ./backend/
|
||||
COPY --from=builder /app/tina/__generated__ ./tina/__generated__
|
||||
ENV NODE_ENV=production
|
||||
EXPOSE 3000
|
||||
CMD ["node", "backend/index.js"]
|
||||
|
||||
+14
-3
@@ -1,12 +1,23 @@
|
||||
// @ts-check
|
||||
import { defineConfig } from 'astro/config';
|
||||
import mdx from '@astrojs/mdx';
|
||||
|
||||
import { loadEnv } from 'vite';
|
||||
import sanity from '@sanity/astro';
|
||||
import react from '@astrojs/react';
|
||||
import tailwindcss from '@tailwindcss/vite';
|
||||
|
||||
const env = loadEnv(process.env.NODE_ENV ?? 'development', process.cwd(), '');
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
integrations: [mdx()],
|
||||
integrations: [
|
||||
sanity({
|
||||
projectId: env.PUBLIC_SANITY_PROJECT_ID,
|
||||
dataset: env.PUBLIC_SANITY_DATASET ?? 'production',
|
||||
studioBasePath: '/studio',
|
||||
useCdn: false,
|
||||
}),
|
||||
react(),
|
||||
],
|
||||
|
||||
vite: {
|
||||
plugins: [tailwindcss()],
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
import express from 'express'
|
||||
import { TinaNodeBackend, LocalBackendAuthProvider, createDatabase, createLocalDatabase } from '@tinacms/datalayer'
|
||||
import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs'
|
||||
import { MongodbLevel } from 'mongodb-level'
|
||||
import { GitHubProvider } from 'tinacms-gitprovider-github'
|
||||
|
||||
const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'
|
||||
const branch = process.env.GITEA_BRANCH || 'main'
|
||||
|
||||
const databaseClient = isLocal
|
||||
? createLocalDatabase()
|
||||
: createDatabase({
|
||||
gitProvider: new GitHubProvider({
|
||||
branch,
|
||||
owner: process.env.GITEA_OWNER,
|
||||
repo: process.env.GITEA_REPO,
|
||||
token: process.env.GITEA_TOKEN,
|
||||
octokitOptions: {
|
||||
baseUrl: 'https://git.idealconnected.com/api/v1',
|
||||
},
|
||||
}),
|
||||
databaseAdapter: new MongodbLevel({
|
||||
collectionName: 'tinacms',
|
||||
dbName: 'tinacms',
|
||||
mongoUri: process.env.MONGODB_URI,
|
||||
}),
|
||||
})
|
||||
|
||||
const authProvider = isLocal
|
||||
? LocalBackendAuthProvider()
|
||||
: AuthJsBackendAuthProvider({
|
||||
authOptions: TinaAuthJSOptions({
|
||||
databaseClient,
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
}),
|
||||
})
|
||||
|
||||
const tinaHandler = TinaNodeBackend({
|
||||
authProvider,
|
||||
databaseClient,
|
||||
})
|
||||
|
||||
const app = express()
|
||||
app.use(express.json())
|
||||
|
||||
// Mount at root — TinaNodeBackend routes /api/tina/* internally
|
||||
app.all('*', (req, res) => tinaHandler(req, res))
|
||||
|
||||
const port = process.env.PORT || 3000
|
||||
app.listen(port, () => console.log(`Tina backend running on port ${port}`))
|
||||
@@ -1,26 +0,0 @@
|
||||
---
|
||||
title: Welcome to Theater Ziefen
|
||||
date: 2026-01-28T00:00:00.000Z
|
||||
excerpt: We're excited to announce our upcoming theatre production in 2029. Stay tuned for more updates!
|
||||
coverImage: /images/theater-placeholder.jpg
|
||||
---
|
||||
|
||||
# Welcome to Theater Ziefen
|
||||
|
||||
We are thrilled to welcome you to the official website of Theater Ziefen. Our theatre production is scheduled to take place in approximately three years, and we can't wait to share this journey with you.
|
||||
|
||||
## What to Expect
|
||||
|
||||
This blog will keep you updated on:
|
||||
|
||||
- Production developments and announcements
|
||||
- Behind-the-scenes insights
|
||||
- Cast and crew introductions
|
||||
- Ticket information (coming soon)
|
||||
- Event schedules
|
||||
|
||||
## Stay Connected
|
||||
|
||||
Make sure to check back regularly for updates, or subscribe to our newsletter to receive the latest news directly in your inbox.
|
||||
|
||||
We look forward to bringing you an unforgettable theatrical experience!
|
||||
@@ -1,27 +0,0 @@
|
||||
---
|
||||
title: About Theater Ziefen
|
||||
description: Learn more about our upcoming theatre production and the team behind it
|
||||
---
|
||||
|
||||
# About Theater Ziefen
|
||||
|
||||
Theater Ziefen is an exciting theatrical production planned for 2029. Our mission is to bring exceptional live theatre to our community and create memorable experiences for audiences of all ages.
|
||||
|
||||
## Our Vision
|
||||
|
||||
We believe in the power of theatre to inspire, entertain, and bring people together. Our upcoming production will showcase local talent and creativity while delivering a professional, high-quality performance.
|
||||
|
||||
## The Production
|
||||
|
||||
Details about our production will be announced in the coming months. Stay tuned to our blog for updates on:
|
||||
|
||||
- Show dates and times
|
||||
- Venue information
|
||||
- Cast announcements
|
||||
- Ticket sales
|
||||
|
||||
## Get Involved
|
||||
|
||||
Interested in being part of Theater Ziefen? We're always looking for passionate individuals to join our team. Whether you're interested in performing, helping backstage, or volunteering, we'd love to hear from you.
|
||||
|
||||
Contact us for more information about opportunities to get involved.
|
||||
@@ -1,92 +0,0 @@
|
||||
---
|
||||
title: Freilichttheater Ziefen 2028
|
||||
hero:
|
||||
title_de: Freilichttheater in Ziefen
|
||||
title_en: Open-Air Theater in Ziefen
|
||||
subtitle_de: '2028 grosses Freilichtspiel geplant. Der Blick auf die Gesellschaft der damaligen Zeit aus der Sicht des in Ziefen aufgewachsenen späteren Pfarrers, Waisenvaters und Mundartautors… Jonas Breitenstein'
|
||||
subtitle_en: 'A grand open-air play planned for 2028. A view of society of that time from the perspective of Jonas Breitenstein, who grew up in Ziefen and later became a pastor, orphanage father, and dialect author.'
|
||||
projekt:
|
||||
title_de: Das Projekt
|
||||
title_en: The Project
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
team:
|
||||
title_de: Wer steckt dahinter
|
||||
title_en: Who's Behind It
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
organizations:
|
||||
- name: Verein J.B.
|
||||
role_de: Hauptverantwortlich für das Theaterprojekt
|
||||
role_en: Main responsibility for the theater project
|
||||
logo: /images/Frontispitz_SW_clean_web.png
|
||||
url: 'https://jonas-breitenstein.ch/verein.html'
|
||||
- name: Verein 4417
|
||||
role_de: Mitorganisator
|
||||
role_en: Co-organizer
|
||||
logo: /images/img_medium.jpg
|
||||
url: 'https://www.ziefen.ch/freizeit-kultur/vereine.html/87/association/23'
|
||||
- name: Bürgergemeinde
|
||||
role_de: Unterstützer
|
||||
role_en: Supporter
|
||||
logo: /images/Wappen Ziefen.png
|
||||
url: 'https://bg-ziefen.ch/'
|
||||
- name: Einwohnergemeinde
|
||||
role_de: Unterstützer
|
||||
role_en: Supporter
|
||||
logo: /images/Wappen Ziefen.png
|
||||
url: 'https://www.ziefen.ch/'
|
||||
stueck:
|
||||
title_de: Das Stück
|
||||
title_en: The Play
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
mitwirkung:
|
||||
title_de: Mitwirkung im Schauspiel
|
||||
title_en: Participation in the Play
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
director_name: Danny Wehrmüller
|
||||
director_email: director@theater-ziefen.ch
|
||||
director_phone: +41 XX XXX XX XX
|
||||
termine:
|
||||
title_de: Termine
|
||||
title_en: Schedule
|
||||
events:
|
||||
- title_de: Casting
|
||||
title_en: Casting
|
||||
date: Frühjahr 2027
|
||||
description_de: Gruppencasting zum gegenseitigen Kennenlernen
|
||||
description_en: Group casting to get to know each other
|
||||
- title_de: Proben
|
||||
title_en: Rehearsals
|
||||
date: Ab Oktober 2027
|
||||
description_de: Beginn der regelmässigen Proben
|
||||
description_en: Start of regular rehearsals
|
||||
- title_de: Intensive Proben
|
||||
title_en: Intensive Rehearsals
|
||||
date: Ab Mai 2028
|
||||
description_de: 'WICHTIG: Keine Ferien oder längere Abwesenheit planen! Mit zwei Probewochenenden.'
|
||||
description_en: 'IMPORTANT: Do not plan vacations or longer absences! Includes two rehearsal weekends.'
|
||||
- title_de: Vorstellungen
|
||||
title_en: Performances
|
||||
date: Juni 2028
|
||||
description_de: Acht bis neun Vorstellungen
|
||||
description_en: Eight to nine performances
|
||||
presse:
|
||||
title_de: Presse
|
||||
title_en: Press
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
gallery:
|
||||
title_de: Foto Galerie
|
||||
title_en: Photo Gallery
|
||||
images: []
|
||||
tickets:
|
||||
title_de: Tickets
|
||||
title_en: Tickets
|
||||
content_de: ''
|
||||
content_en: ''
|
||||
ticket_url: ''
|
||||
---
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"users": [
|
||||
{
|
||||
"name": "Admin",
|
||||
"email": "admin@theater-jb.ch",
|
||||
"username": "admin",
|
||||
"password": "changeme"
|
||||
}
|
||||
]
|
||||
}
|
||||
Generated
+10249
-19493
File diff suppressed because it is too large
Load Diff
+17
-20
@@ -3,35 +3,32 @@
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "tinacms dev -c \"astro dev\"",
|
||||
"dev": "astro dev",
|
||||
"build": "astro check && astro build",
|
||||
"build:tina": "tinacms build && astro check && astro build",
|
||||
"build:docker": "tinacms build && astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
"astro": "astro",
|
||||
"migrate": "node --env-file=.env scripts/migrate-to-sanity.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/check": "^0.9.9",
|
||||
"@astrojs/mdx": "^5.0.4",
|
||||
"@astrojs/react": "^5.0.4",
|
||||
"@portabletext/to-html": "^5.0.2",
|
||||
"@sanity/astro": "^3.4.0",
|
||||
"@sanity/client": "^7.22.0",
|
||||
"@sanity/image-url": "^2.1.1",
|
||||
"@sanity/vision": "^3.0.0",
|
||||
"@tailwindcss/vite": "^4.2.4",
|
||||
"@tinacms/cli": "^2.2.5",
|
||||
"@tinacms/datalayer": "^2.0.18",
|
||||
"fs-extra": "^9.1.0",
|
||||
"astro": "^6.2.1",
|
||||
"express": "^5.2.1",
|
||||
"mongodb-level": "^0.0.4",
|
||||
"next": "^15.5.15",
|
||||
"next-auth": "^4.24.14",
|
||||
"react": "^19.2.5",
|
||||
"react-dom": "^19.2.5",
|
||||
"slate": "^0.124.1",
|
||||
"slate-dom": "^0.124.1",
|
||||
"slate-hyperscript": "^0.100.0",
|
||||
"slate-react": "^0.124.0",
|
||||
"react-is": "^19.0.0",
|
||||
"sanity": "^5.24.0",
|
||||
"styled-components": "^6.1.19",
|
||||
"tailwindcss": "^4.2.4",
|
||||
"tinacms": "^3.7.5",
|
||||
"tinacms-authjs": "^21.0.5",
|
||||
"tinacms-gitprovider-github": "^4.1.5",
|
||||
"typescript": "^5.6.3"
|
||||
"typescript": "^5.6.3",
|
||||
"vite": "^7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@portabletext/markdown": "^1.2.0"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
---
|
||||
import { urlFor } from '../sanity/imageUrl';
|
||||
|
||||
interface Props {
|
||||
images: string[];
|
||||
images: any[];
|
||||
}
|
||||
|
||||
const { images } = Astro.props;
|
||||
@@ -10,7 +12,7 @@ const { images } = Astro.props;
|
||||
<div class="grid grid-cols-[repeat(auto-fill,minmax(300px,1fr))] md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))] gap-[--spacing-md] md:gap-[--spacing-sm] mt-[--spacing-lg]">
|
||||
{images.map((image) => (
|
||||
<div class="relative overflow-hidden rounded-lg shadow-[0_4px_10px_rgba(0,0,0,0.2)] transition-all duration-300 aspect-[4/3] hover:-translate-y-1 hover:shadow-[0_8px_20px_rgba(0,0,0,0.3)] group">
|
||||
<img src={image} alt="Theater photo" loading="lazy" class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" />
|
||||
<img src={urlFor(image)} alt="Theater photo" loading="lazy" class="w-full h-full object-cover transition-transform duration-300 group-hover:scale-105" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
/// <reference types="astro/client" />
|
||||
/// <reference types="@sanity/astro/module" />
|
||||
@@ -1,21 +1,23 @@
|
||||
---
|
||||
import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import { sanityClient } from '../sanity/client';
|
||||
import { renderPortableText } from '../sanity/portableText';
|
||||
import { pageBySlugQuery } from '../sanity/queries';
|
||||
|
||||
const { Content, frontmatter } = await import('../../content/pages/about.mdx');
|
||||
const page = await sanityClient.fetch(pageBySlugQuery, { slug: 'about' });
|
||||
const bodyHtml = renderPortableText(page?.body);
|
||||
---
|
||||
|
||||
<BaseLayout title={frontmatter.title} description={frontmatter.description}>
|
||||
<BaseLayout title={page?.title} description={page?.description}>
|
||||
<div class="page-header">
|
||||
<div class="container">
|
||||
<h1>{frontmatter.title}</h1>
|
||||
<h1>{page?.title}</h1>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="page-content">
|
||||
<div class="container">
|
||||
<div class="prose">
|
||||
<Content />
|
||||
</div>
|
||||
<div class="prose" set:html={bodyHtml} />
|
||||
</div>
|
||||
</div>
|
||||
</BaseLayout>
|
||||
|
||||
+17
-19
@@ -1,30 +1,30 @@
|
||||
---
|
||||
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 allPosts = import.meta.glob<{ Content: any; frontmatter: Record<string, any> }>('../../../content/blog/*.mdx', { eager: true });
|
||||
|
||||
return Object.entries(allPosts).map(([path, post]) => {
|
||||
const slug = path.split('/').pop()?.replace('.mdx', '') || '';
|
||||
return {
|
||||
params: { slug },
|
||||
const posts = await sanityClient.fetch(allPostsQuery);
|
||||
return posts.map((post: any) => ({
|
||||
params: { slug: post.slug },
|
||||
props: { post },
|
||||
};
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
const { post } = Astro.props as { post: { Content: any; frontmatter: Record<string, any> } };
|
||||
const { Content, frontmatter } = post;
|
||||
const { post } = Astro.props as { post: any };
|
||||
const bodyHtml = renderPortableText(post.body);
|
||||
---
|
||||
|
||||
<BaseLayout title={frontmatter.title} description={frontmatter.excerpt}>
|
||||
<BaseLayout title={post.title} description={post.excerpt}>
|
||||
<article class="blog-post">
|
||||
<header class="post-header">
|
||||
<div class="container">
|
||||
<h1>{frontmatter.title}</h1>
|
||||
<h1>{post.title}</h1>
|
||||
<p class="post-meta">
|
||||
<time datetime={frontmatter.date}>
|
||||
{new Date(frontmatter.date).toLocaleDateString('de-DE', {
|
||||
<time datetime={post.date}>
|
||||
{new Date(post.date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
@@ -34,17 +34,15 @@ const { Content, frontmatter } = post;
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{frontmatter.coverImage && (
|
||||
{post.coverImage && (
|
||||
<div class="post-cover">
|
||||
<img src={frontmatter.coverImage} alt={frontmatter.title} />
|
||||
<img src={urlFor(post.coverImage)} alt={post.title} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class="post-content">
|
||||
<div class="container">
|
||||
<div class="prose">
|
||||
<Content />
|
||||
</div>
|
||||
<div class="prose" set:html={bodyHtml} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+12
-16
@@ -1,14 +1,10 @@
|
||||
---
|
||||
import BaseLayout from '../../layouts/BaseLayout.astro';
|
||||
import { sanityClient } from '../../sanity/client';
|
||||
import { urlFor } from '../../sanity/imageUrl';
|
||||
import { allPostsQuery } from '../../sanity/queries';
|
||||
|
||||
const allPostFiles = import.meta.glob<{ frontmatter: Record<string, any> }>('../../../content/blog/*.mdx', { eager: true });
|
||||
const allPosts = Object.entries(allPostFiles).map(([path, post]) => ({
|
||||
...post,
|
||||
slug: path.split('/').pop()?.replace('.mdx', '') || ''
|
||||
}));
|
||||
const sortedPosts = allPosts.sort(
|
||||
(a, b) => new Date(b.frontmatter.date).valueOf() - new Date(a.frontmatter.date).valueOf()
|
||||
);
|
||||
const allPosts = await sanityClient.fetch(allPostsQuery);
|
||||
---
|
||||
|
||||
<BaseLayout title="Blog" description="Latest news and updates from Theater Ziefen">
|
||||
@@ -21,33 +17,33 @@ const sortedPosts = allPosts.sort(
|
||||
|
||||
<div class="blog-content">
|
||||
<div class="container">
|
||||
{sortedPosts.length === 0 ? (
|
||||
{allPosts.length === 0 ? (
|
||||
<p class="no-posts">No blog posts yet. Check back soon!</p>
|
||||
) : (
|
||||
<div class="posts-list">
|
||||
{sortedPosts.map((post) => (
|
||||
{allPosts.map((post: any) => (
|
||||
<article class="post-item">
|
||||
{post.frontmatter.coverImage && (
|
||||
{post.coverImage && (
|
||||
<div class="post-image">
|
||||
<img src={post.frontmatter.coverImage} alt={post.frontmatter.title} />
|
||||
<img src={urlFor(post.coverImage)} alt={post.title} />
|
||||
</div>
|
||||
)}
|
||||
<div class="post-content">
|
||||
<h2>
|
||||
<a href={`/blog/${post.slug}`}>
|
||||
{post.frontmatter.title}
|
||||
{post.title}
|
||||
</a>
|
||||
</h2>
|
||||
<p class="post-meta">
|
||||
<time datetime={post.frontmatter.date}>
|
||||
{new Date(post.frontmatter.date).toLocaleDateString('de-DE', {
|
||||
<time datetime={post.date}>
|
||||
{new Date(post.date).toLocaleDateString('de-DE', {
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
})}
|
||||
</time>
|
||||
</p>
|
||||
<p class="post-excerpt">{post.frontmatter.excerpt}</p>
|
||||
<p class="post-excerpt">{post.excerpt}</p>
|
||||
<a href={`/blog/${post.slug}`} class="read-more">
|
||||
Read full post →
|
||||
</a>
|
||||
|
||||
+18
-45
@@ -3,39 +3,12 @@ import BaseLayout from '../layouts/BaseLayout.astro';
|
||||
import AnchorNav from '../components/AnchorNav.astro';
|
||||
import TheaterSection from '../components/TheaterSection.astro';
|
||||
import PhotoGallery from '../components/PhotoGallery.astro';
|
||||
import { sanityClient } from '../sanity/client';
|
||||
import { renderPortableText } from '../sanity/portableText';
|
||||
import { urlFor } from '../sanity/imageUrl';
|
||||
import { theaterHomepageQuery } from '../sanity/queries';
|
||||
|
||||
// Import theater homepage content
|
||||
const theaterFiles = import.meta.glob('../../content/theater/*.mdx', { eager: true });
|
||||
const theaterHomepage = Object.values(theaterFiles)[0] as any;
|
||||
const content = theaterHomepage?.frontmatter || {};
|
||||
|
||||
// Helper function to render rich-text content
|
||||
function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
if (!richText || !richText.children) return '';
|
||||
|
||||
let html = '';
|
||||
richText.children.forEach((child: any) => {
|
||||
if (child.type === 'p') {
|
||||
html += '<p>';
|
||||
child.children.forEach((textNode: any) => {
|
||||
if (textNode.type === 'text') {
|
||||
html += textNode.text;
|
||||
}
|
||||
});
|
||||
html += '</p>';
|
||||
} else if (child.type === 'h3') {
|
||||
html += '<h3>';
|
||||
child.children.forEach((textNode: any) => {
|
||||
if (textNode.type === 'text') {
|
||||
html += textNode.text;
|
||||
}
|
||||
});
|
||||
html += '</h3>';
|
||||
}
|
||||
});
|
||||
|
||||
return html;
|
||||
}
|
||||
const content = await sanityClient.fetch(theaterHomepageQuery) ?? {};
|
||||
---
|
||||
|
||||
<BaseLayout title={content.title || 'Freilichttheater Ziefen 2028'}>
|
||||
@@ -69,8 +42,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.projekt?.title_en || 'The Project'}
|
||||
variant="light"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.projekt?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.projekt?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.projekt?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.projekt?.content_en)} />
|
||||
</TheaterSection>
|
||||
|
||||
<!-- Section 2: Wer steckt dahinter -->
|
||||
@@ -80,8 +53,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.team?.title_en || "Who's Behind It"}
|
||||
variant="dark"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.team?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.team?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.team?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.team?.content_en)} />
|
||||
|
||||
{content.team?.organizations && content.team.organizations.length > 0 && (
|
||||
<div class="grid grid-cols-1 md:grid-cols-[repeat(auto-fit,minmax(250px,1fr))] gap-12 mt-16">
|
||||
@@ -90,7 +63,7 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
<Fragment>
|
||||
{org.logo && (
|
||||
<div class="mb-4 flex justify-center">
|
||||
<img src={org.logo} alt={`${org.name} logo`} class="h-20 w-auto object-contain" />
|
||||
<img src={urlFor(org.logo)} alt={`${org.name} logo`} class="h-20 w-auto object-contain" />
|
||||
</div>
|
||||
)}
|
||||
<h3 class="text-3xl mb-2 text-white">{org.name}</h3>
|
||||
@@ -125,8 +98,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.stueck?.title_en || 'The Play'}
|
||||
variant="light"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.stueck?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.stueck?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.stueck?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.stueck?.content_en)} />
|
||||
</TheaterSection>
|
||||
|
||||
<!-- Section 4: Mitwirkung -->
|
||||
@@ -136,8 +109,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.mitwirkung?.title_en || 'Participation in the Play'}
|
||||
variant="dark"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.mitwirkung?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.mitwirkung?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.mitwirkung?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.mitwirkung?.content_en)} />
|
||||
|
||||
{content.mitwirkung?.director_name && (
|
||||
<div class="mt-16 p-12 bg-white/10 rounded-lg text-center">
|
||||
@@ -212,8 +185,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.presse?.title_en || 'Press'}
|
||||
variant="dark"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.presse?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.presse?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.presse?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.presse?.content_en)} />
|
||||
</TheaterSection>
|
||||
|
||||
<!-- Section 7: Foto Galerie -->
|
||||
@@ -233,8 +206,8 @@ function renderRichText(richText: any, lang: 'de' | 'en'): string {
|
||||
titleEn={content.tickets?.title_en || 'Tickets'}
|
||||
variant="dark"
|
||||
>
|
||||
<div class="content-de" data-lang="de" set:html={renderRichText(content.tickets?.content_de, 'de')} />
|
||||
<div class="content-en" data-lang="en" set:html={renderRichText(content.tickets?.content_en, 'en')} />
|
||||
<div class="content-de" data-lang="de" set:html={renderPortableText(content.tickets?.content_de)} />
|
||||
<div class="content-en" data-lang="en" set:html={renderPortableText(content.tickets?.content_en)} />
|
||||
|
||||
{content.tickets?.ticket_url && (
|
||||
<div class="mt-[--spacing-lg] text-center">
|
||||
|
||||
@@ -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' }),
|
||||
],
|
||||
}),
|
||||
],
|
||||
})
|
||||
Generated
-1
File diff suppressed because one or more lines are too long
Generated
-1
@@ -1 +0,0 @@
|
||||
{"DocumentConnection":{"type":"DocumentConnection","resolveType":"multiCollectionDocumentList","collections":["user","post","page","theaterHomepage"]},"Node":{"type":"Node","resolveType":"nodeDocument"},"DocumentNode":{"type":"DocumentNode","resolveType":"multiCollectionDocument","createDocument":"create","updateDocument":"update"},"User":{"type":"User","resolveType":"collectionDocument","collection":"user","createUser":"create","updateUser":"update"},"UserConnection":{"type":"UserConnection","resolveType":"collectionDocumentList","collection":"user"},"Post":{"type":"Post","resolveType":"collectionDocument","collection":"post","createPost":"create","updatePost":"update"},"PostConnection":{"type":"PostConnection","resolveType":"collectionDocumentList","collection":"post"},"Page":{"type":"Page","resolveType":"collectionDocument","collection":"page","createPage":"create","updatePage":"update"},"PageConnection":{"type":"PageConnection","resolveType":"collectionDocumentList","collection":"page"},"TheaterHomepage":{"type":"TheaterHomepage","resolveType":"collectionDocument","collection":"theaterHomepage","createTheaterHomepage":"create","updateTheaterHomepage":"update"},"TheaterHomepageConnection":{"type":"TheaterHomepageConnection","resolveType":"collectionDocumentList","collection":"theaterHomepage"}}
|
||||
Generated
-1
File diff suppressed because one or more lines are too long
Generated
-5
@@ -1,5 +0,0 @@
|
||||
import { createClient } from "tinacms/dist/client";
|
||||
import { queries } from "./types";
|
||||
export const client = createClient({ url: '/api/tina/gql', token: 'undefined', queries, });
|
||||
export default client;
|
||||
|
||||
Generated
-494
@@ -1,494 +0,0 @@
|
||||
// tina/config.ts
|
||||
import { defineConfig } from "tinacms";
|
||||
import { UsernamePasswordAuthJSProvider, TinaUserCollection } from "tinacms-authjs/dist/tinacms";
|
||||
var config_default = defineConfig({
|
||||
contentApiUrlOverride: "/api/tina/gql",
|
||||
authProvider: new UsernamePasswordAuthJSProvider(),
|
||||
build: {
|
||||
outputFolder: "admin",
|
||||
publicFolder: "public"
|
||||
},
|
||||
media: {
|
||||
tina: {
|
||||
mediaRoot: "images",
|
||||
publicFolder: "public",
|
||||
static: false
|
||||
}
|
||||
},
|
||||
schema: {
|
||||
collections: [
|
||||
TinaUserCollection,
|
||||
{
|
||||
name: "post",
|
||||
label: "Blog Posts",
|
||||
path: "content/blog",
|
||||
format: "mdx",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Title",
|
||||
isTitle: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "datetime",
|
||||
name: "date",
|
||||
label: "Publication Date",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "excerpt",
|
||||
label: "Excerpt",
|
||||
required: true,
|
||||
description: "Short summary of the blog post"
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "coverImage",
|
||||
label: "Cover Image",
|
||||
description: "Featured image for the blog post"
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "body",
|
||||
label: "Body",
|
||||
isBody: true,
|
||||
required: true
|
||||
}
|
||||
],
|
||||
defaultItem: () => {
|
||||
return {
|
||||
title: "New Blog Post",
|
||||
date: (/* @__PURE__ */ new Date()).toISOString(),
|
||||
excerpt: ""
|
||||
};
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "page",
|
||||
label: "Pages",
|
||||
path: "content/pages",
|
||||
format: "mdx",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Title",
|
||||
isTitle: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description",
|
||||
label: "Description",
|
||||
description: "Meta description for SEO"
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "body",
|
||||
label: "Content",
|
||||
isBody: true,
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: "theaterHomepage",
|
||||
label: "Theater Homepage",
|
||||
path: "content/theater",
|
||||
format: "mdx",
|
||||
ui: {
|
||||
allowedActions: {
|
||||
create: false,
|
||||
delete: false
|
||||
}
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Page Title",
|
||||
isTitle: true,
|
||||
required: true
|
||||
},
|
||||
// Hero Section
|
||||
{
|
||||
type: "object",
|
||||
name: "hero",
|
||||
label: "Hero Section",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "subtitle_de",
|
||||
label: "Subtitle (German)",
|
||||
required: true,
|
||||
ui: {
|
||||
component: "textarea"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "subtitle_en",
|
||||
label: "Subtitle (English)",
|
||||
required: true,
|
||||
ui: {
|
||||
component: "textarea"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 1: Das Projekt
|
||||
{
|
||||
type: "object",
|
||||
name: "projekt",
|
||||
label: "Section 1: Das Projekt / The Project",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 2: Wer steckt dahinter
|
||||
{
|
||||
type: "object",
|
||||
name: "team",
|
||||
label: "Section 2: Wer steckt dahinter / Who's Behind It",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
name: "organizations",
|
||||
label: "Organizations",
|
||||
list: true,
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "name",
|
||||
label: "Name"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "role_de",
|
||||
label: "Role (German)"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "role_en",
|
||||
label: "Role (English)"
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "logo",
|
||||
label: "Logo",
|
||||
description: "Organization logo image (SVG, PNG, JPG supported)"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "url",
|
||||
label: "Website URL",
|
||||
description: "Organization website link"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 3: Das Stück
|
||||
{
|
||||
type: "object",
|
||||
name: "stueck",
|
||||
label: "Section 3: Das St\xFCck / The Play",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 4: Mitwirkung
|
||||
{
|
||||
type: "object",
|
||||
name: "mitwirkung",
|
||||
label: "Section 4: Mitwirkung / Participation",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_name",
|
||||
label: "Director Name"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_email",
|
||||
label: "Director Email"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_phone",
|
||||
label: "Director Phone"
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 5: Termine
|
||||
{
|
||||
type: "object",
|
||||
name: "termine",
|
||||
label: "Section 5: Termine / Schedule",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
name: "events",
|
||||
label: "Events",
|
||||
list: true,
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Event Title (German)"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Event Title (English)"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "date",
|
||||
label: "Date/Period"
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description_de",
|
||||
label: "Description (German)",
|
||||
ui: {
|
||||
component: "textarea"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description_en",
|
||||
label: "Description (English)",
|
||||
ui: {
|
||||
component: "textarea"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 6: Presse
|
||||
{
|
||||
type: "object",
|
||||
name: "presse",
|
||||
label: "Section 6: Presse / Press",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 7: Foto Galerie
|
||||
{
|
||||
type: "object",
|
||||
name: "gallery",
|
||||
label: "Section 7: Foto Galerie / Photo Gallery",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "images",
|
||||
label: "Gallery Images",
|
||||
list: true
|
||||
}
|
||||
]
|
||||
},
|
||||
// Section 8: Tickets
|
||||
{
|
||||
type: "object",
|
||||
name: "tickets",
|
||||
label: "Section 8: Tickets",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "ticket_url",
|
||||
label: "Ticket Purchase URL",
|
||||
description: "External link to ticket partner"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
export {
|
||||
config_default as default
|
||||
};
|
||||
Generated
-63
@@ -1,63 +0,0 @@
|
||||
// @ts-nocheck
|
||||
import { resolve } from "@tinacms/datalayer";
|
||||
import type { TinaClient } from "tinacms/dist/client";
|
||||
|
||||
import { queries } from "./types";
|
||||
import database from "../database";
|
||||
|
||||
export async function databaseRequest({ query, variables, user }) {
|
||||
const result = await resolve({
|
||||
config: {
|
||||
useRelativeMedia: true,
|
||||
},
|
||||
database,
|
||||
query,
|
||||
variables,
|
||||
verbose: true,
|
||||
ctxUser: user,
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export async function authenticate({ username, password }) {
|
||||
return databaseRequest({
|
||||
query: `query auth($username:String!, $password:String!) {
|
||||
authenticate(sub:$username, password:$password) {
|
||||
id:username name email _password: password { passwordChangeRequired }
|
||||
}
|
||||
}`,
|
||||
variables: { username, password },
|
||||
})
|
||||
}
|
||||
|
||||
export async function authorize(user: { sub: string }) {
|
||||
return databaseRequest({
|
||||
query: `query authz { authorize { id:username name email _password: password { passwordChangeRequired }} }`,
|
||||
variables: {},
|
||||
user
|
||||
})
|
||||
}
|
||||
|
||||
function createDatabaseClient<GenQueries = Record<string, unknown>>({
|
||||
queries,
|
||||
}: {
|
||||
queries: (client: {
|
||||
request: TinaClient<GenQueries>["request"];
|
||||
}) => GenQueries;
|
||||
}) {
|
||||
const request = async ({ query, variables, user }) => {
|
||||
const data = await databaseRequest({ query, variables, user });
|
||||
return { data: data.data as any, query, variables, errors: data.errors || null };
|
||||
};
|
||||
const q = queries({
|
||||
request,
|
||||
});
|
||||
return { queries: q, request, authenticate, authorize };
|
||||
}
|
||||
|
||||
export const databaseClient = createDatabaseClient({ queries });
|
||||
|
||||
export const client = databaseClient;
|
||||
|
||||
export default databaseClient;
|
||||
Generated
-114
@@ -1,114 +0,0 @@
|
||||
fragment UserParts on User {
|
||||
__typename
|
||||
users {
|
||||
__typename
|
||||
username
|
||||
name
|
||||
email
|
||||
password {
|
||||
value
|
||||
passwordChangeRequired
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fragment PostParts on Post {
|
||||
__typename
|
||||
title
|
||||
date
|
||||
excerpt
|
||||
coverImage
|
||||
body
|
||||
}
|
||||
|
||||
fragment PageParts on Page {
|
||||
__typename
|
||||
title
|
||||
description
|
||||
body
|
||||
}
|
||||
|
||||
fragment TheaterHomepageParts on TheaterHomepage {
|
||||
__typename
|
||||
title
|
||||
hero {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
subtitle_de
|
||||
subtitle_en
|
||||
}
|
||||
projekt {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
}
|
||||
team {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
organizations {
|
||||
__typename
|
||||
name
|
||||
role_de
|
||||
role_en
|
||||
logo
|
||||
url
|
||||
}
|
||||
}
|
||||
stueck {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
}
|
||||
mitwirkung {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
director_name
|
||||
director_email
|
||||
director_phone
|
||||
}
|
||||
termine {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
events {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
date
|
||||
description_de
|
||||
description_en
|
||||
}
|
||||
}
|
||||
presse {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
}
|
||||
gallery {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
images
|
||||
}
|
||||
tickets {
|
||||
__typename
|
||||
title_de
|
||||
title_en
|
||||
content_de
|
||||
content_en
|
||||
ticket_url
|
||||
}
|
||||
}
|
||||
Generated
-219
@@ -1,219 +0,0 @@
|
||||
query user($relativePath: String!) {
|
||||
user(relativePath: $relativePath) {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...UserParts
|
||||
}
|
||||
}
|
||||
|
||||
query userConnection($before: String, $after: String, $first: Float, $last: Float, $sort: String, $filter: UserFilter) {
|
||||
userConnection(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
sort: $sort
|
||||
filter: $filter
|
||||
) {
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...UserParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query post($relativePath: String!) {
|
||||
post(relativePath: $relativePath) {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...PostParts
|
||||
}
|
||||
}
|
||||
|
||||
query postConnection($before: String, $after: String, $first: Float, $last: Float, $sort: String, $filter: PostFilter) {
|
||||
postConnection(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
sort: $sort
|
||||
filter: $filter
|
||||
) {
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...PostParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query page($relativePath: String!) {
|
||||
page(relativePath: $relativePath) {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...PageParts
|
||||
}
|
||||
}
|
||||
|
||||
query pageConnection($before: String, $after: String, $first: Float, $last: Float, $sort: String, $filter: PageFilter) {
|
||||
pageConnection(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
sort: $sort
|
||||
filter: $filter
|
||||
) {
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...PageParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
query theaterHomepage($relativePath: String!) {
|
||||
theaterHomepage(relativePath: $relativePath) {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...TheaterHomepageParts
|
||||
}
|
||||
}
|
||||
|
||||
query theaterHomepageConnection($before: String, $after: String, $first: Float, $last: Float, $sort: String, $filter: TheaterHomepageFilter) {
|
||||
theaterHomepageConnection(
|
||||
before: $before
|
||||
after: $after
|
||||
first: $first
|
||||
last: $last
|
||||
sort: $sort
|
||||
filter: $filter
|
||||
) {
|
||||
pageInfo {
|
||||
hasPreviousPage
|
||||
hasNextPage
|
||||
startCursor
|
||||
endCursor
|
||||
}
|
||||
totalCount
|
||||
edges {
|
||||
cursor
|
||||
node {
|
||||
... on Document {
|
||||
_sys {
|
||||
filename
|
||||
basename
|
||||
hasReferences
|
||||
breadcrumbs
|
||||
path
|
||||
relativePath
|
||||
extension
|
||||
}
|
||||
id
|
||||
}
|
||||
...TheaterHomepageParts
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Generated
-589
@@ -1,589 +0,0 @@
|
||||
# DO NOT MODIFY THIS FILE. This file is automatically generated by Tina
|
||||
"""References another document, used as a foreign key"""
|
||||
scalar Reference
|
||||
|
||||
""""""
|
||||
scalar JSON
|
||||
|
||||
type SystemInfo {
|
||||
filename: String!
|
||||
title: String
|
||||
basename: String!
|
||||
hasReferences: Boolean
|
||||
breadcrumbs(excludeExtension: Boolean): [String!]!
|
||||
path: String!
|
||||
relativePath: String!
|
||||
extension: String!
|
||||
template: String!
|
||||
collection: Collection!
|
||||
}
|
||||
|
||||
type Folder {
|
||||
name: String!
|
||||
path: String!
|
||||
}
|
||||
|
||||
type PageInfo {
|
||||
hasPreviousPage: Boolean!
|
||||
hasNextPage: Boolean!
|
||||
startCursor: String!
|
||||
endCursor: String!
|
||||
}
|
||||
|
||||
""""""
|
||||
interface Node {
|
||||
id: ID!
|
||||
}
|
||||
|
||||
""""""
|
||||
interface Document {
|
||||
id: ID!
|
||||
_sys: SystemInfo
|
||||
_values: JSON!
|
||||
}
|
||||
|
||||
"""A relay-compliant pagination connection"""
|
||||
interface Connection {
|
||||
totalCount: Float!
|
||||
pageInfo: PageInfo!
|
||||
}
|
||||
|
||||
type Query {
|
||||
getOptimizedQuery(queryString: String!): String
|
||||
collection(collection: String): Collection!
|
||||
collections: [Collection!]!
|
||||
node(id: String): Node!
|
||||
document(collection: String, relativePath: String): DocumentNode!
|
||||
user(relativePath: String): User!
|
||||
authenticate(sub: String!, password: String!): UserUsers
|
||||
authorize: UserUsers
|
||||
userConnection(before: String, after: String, first: Float, last: Float, sort: String, filter: UserFilter): UserConnection!
|
||||
post(relativePath: String): Post!
|
||||
postConnection(before: String, after: String, first: Float, last: Float, sort: String, filter: PostFilter): PostConnection!
|
||||
page(relativePath: String): Page!
|
||||
pageConnection(before: String, after: String, first: Float, last: Float, sort: String, filter: PageFilter): PageConnection!
|
||||
theaterHomepage(relativePath: String): TheaterHomepage!
|
||||
theaterHomepageConnection(before: String, after: String, first: Float, last: Float, sort: String, filter: TheaterHomepageFilter): TheaterHomepageConnection!
|
||||
}
|
||||
|
||||
input DocumentFilter {
|
||||
user: UserFilter
|
||||
post: PostFilter
|
||||
page: PageFilter
|
||||
theaterHomepage: TheaterHomepageFilter
|
||||
}
|
||||
|
||||
type DocumentConnectionEdges {
|
||||
cursor: String!
|
||||
node: DocumentNode
|
||||
}
|
||||
|
||||
type DocumentConnection implements Connection {
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Float!
|
||||
edges: [DocumentConnectionEdges]
|
||||
}
|
||||
|
||||
type Collection {
|
||||
name: String!
|
||||
slug: String!
|
||||
label: String
|
||||
path: String!
|
||||
format: String
|
||||
matches: String
|
||||
templates: [JSON]
|
||||
fields: [JSON]
|
||||
documents(before: String, after: String, first: Float, last: Float, sort: String, filter: DocumentFilter, folder: String): DocumentConnection!
|
||||
}
|
||||
|
||||
union DocumentNode = User | Post | Page | TheaterHomepage | Folder
|
||||
|
||||
type UserUsersPassword {
|
||||
value: String!
|
||||
passwordChangeRequired: Boolean
|
||||
}
|
||||
|
||||
type UserUsers {
|
||||
username: String!
|
||||
name: String
|
||||
email: String
|
||||
password: UserUsersPassword!
|
||||
}
|
||||
|
||||
type User implements Node & Document {
|
||||
users: [UserUsers]
|
||||
id: ID!
|
||||
_sys: SystemInfo!
|
||||
_values: JSON!
|
||||
}
|
||||
|
||||
input StringFilter {
|
||||
startsWith: String
|
||||
eq: String
|
||||
exists: Boolean
|
||||
in: [String]
|
||||
}
|
||||
|
||||
input UserUsersFilter {
|
||||
username: StringFilter
|
||||
name: StringFilter
|
||||
email: StringFilter
|
||||
}
|
||||
|
||||
input UserFilter {
|
||||
users: UserUsersFilter
|
||||
}
|
||||
|
||||
type UserConnectionEdges {
|
||||
cursor: String!
|
||||
node: User
|
||||
}
|
||||
|
||||
type UserConnection implements Connection {
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Float!
|
||||
edges: [UserConnectionEdges]
|
||||
}
|
||||
|
||||
type Post implements Node & Document {
|
||||
title: String!
|
||||
date: String!
|
||||
excerpt: String!
|
||||
coverImage: String
|
||||
body: JSON!
|
||||
id: ID!
|
||||
_sys: SystemInfo!
|
||||
_values: JSON!
|
||||
}
|
||||
|
||||
input DatetimeFilter {
|
||||
after: String
|
||||
before: String
|
||||
eq: String
|
||||
exists: Boolean
|
||||
in: [String]
|
||||
}
|
||||
|
||||
input ImageFilter {
|
||||
startsWith: String
|
||||
eq: String
|
||||
exists: Boolean
|
||||
in: [String]
|
||||
}
|
||||
|
||||
input RichTextFilter {
|
||||
startsWith: String
|
||||
eq: String
|
||||
exists: Boolean
|
||||
}
|
||||
|
||||
input PostFilter {
|
||||
title: StringFilter
|
||||
date: DatetimeFilter
|
||||
excerpt: StringFilter
|
||||
coverImage: ImageFilter
|
||||
body: RichTextFilter
|
||||
}
|
||||
|
||||
type PostConnectionEdges {
|
||||
cursor: String!
|
||||
node: Post
|
||||
}
|
||||
|
||||
type PostConnection implements Connection {
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Float!
|
||||
edges: [PostConnectionEdges]
|
||||
}
|
||||
|
||||
type Page implements Node & Document {
|
||||
title: String!
|
||||
description: String
|
||||
body: JSON!
|
||||
id: ID!
|
||||
_sys: SystemInfo!
|
||||
_values: JSON!
|
||||
}
|
||||
|
||||
input PageFilter {
|
||||
title: StringFilter
|
||||
description: StringFilter
|
||||
body: RichTextFilter
|
||||
}
|
||||
|
||||
type PageConnectionEdges {
|
||||
cursor: String!
|
||||
node: Page
|
||||
}
|
||||
|
||||
type PageConnection implements Connection {
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Float!
|
||||
edges: [PageConnectionEdges]
|
||||
}
|
||||
|
||||
type TheaterHomepageHero {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
subtitle_de: String!
|
||||
subtitle_en: String!
|
||||
}
|
||||
|
||||
type TheaterHomepageProjekt {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
}
|
||||
|
||||
type TheaterHomepageTeamOrganizations {
|
||||
name: String
|
||||
role_de: String
|
||||
role_en: String
|
||||
logo: String
|
||||
url: String
|
||||
}
|
||||
|
||||
type TheaterHomepageTeam {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
organizations: [TheaterHomepageTeamOrganizations]
|
||||
}
|
||||
|
||||
type TheaterHomepageStueck {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
}
|
||||
|
||||
type TheaterHomepageMitwirkung {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
director_name: String
|
||||
director_email: String
|
||||
director_phone: String
|
||||
}
|
||||
|
||||
type TheaterHomepageTermineEvents {
|
||||
title_de: String
|
||||
title_en: String
|
||||
date: String
|
||||
description_de: String
|
||||
description_en: String
|
||||
}
|
||||
|
||||
type TheaterHomepageTermine {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
events: [TheaterHomepageTermineEvents]
|
||||
}
|
||||
|
||||
type TheaterHomepagePresse {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
}
|
||||
|
||||
type TheaterHomepageGallery {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
images: [String]
|
||||
}
|
||||
|
||||
type TheaterHomepageTickets {
|
||||
title_de: String!
|
||||
title_en: String!
|
||||
content_de: JSON!
|
||||
content_en: JSON!
|
||||
ticket_url: String
|
||||
}
|
||||
|
||||
type TheaterHomepage implements Node & Document {
|
||||
title: String!
|
||||
hero: TheaterHomepageHero
|
||||
projekt: TheaterHomepageProjekt
|
||||
team: TheaterHomepageTeam
|
||||
stueck: TheaterHomepageStueck
|
||||
mitwirkung: TheaterHomepageMitwirkung
|
||||
termine: TheaterHomepageTermine
|
||||
presse: TheaterHomepagePresse
|
||||
gallery: TheaterHomepageGallery
|
||||
tickets: TheaterHomepageTickets
|
||||
id: ID!
|
||||
_sys: SystemInfo!
|
||||
_values: JSON!
|
||||
}
|
||||
|
||||
input TheaterHomepageHeroFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
subtitle_de: StringFilter
|
||||
subtitle_en: StringFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageProjektFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageTeamOrganizationsFilter {
|
||||
name: StringFilter
|
||||
role_de: StringFilter
|
||||
role_en: StringFilter
|
||||
logo: ImageFilter
|
||||
url: StringFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageTeamFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
organizations: TheaterHomepageTeamOrganizationsFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageStueckFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageMitwirkungFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
director_name: StringFilter
|
||||
director_email: StringFilter
|
||||
director_phone: StringFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageTermineEventsFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
date: StringFilter
|
||||
description_de: StringFilter
|
||||
description_en: StringFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageTermineFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
events: TheaterHomepageTermineEventsFilter
|
||||
}
|
||||
|
||||
input TheaterHomepagePresseFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageGalleryFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
images: ImageFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageTicketsFilter {
|
||||
title_de: StringFilter
|
||||
title_en: StringFilter
|
||||
content_de: RichTextFilter
|
||||
content_en: RichTextFilter
|
||||
ticket_url: StringFilter
|
||||
}
|
||||
|
||||
input TheaterHomepageFilter {
|
||||
title: StringFilter
|
||||
hero: TheaterHomepageHeroFilter
|
||||
projekt: TheaterHomepageProjektFilter
|
||||
team: TheaterHomepageTeamFilter
|
||||
stueck: TheaterHomepageStueckFilter
|
||||
mitwirkung: TheaterHomepageMitwirkungFilter
|
||||
termine: TheaterHomepageTermineFilter
|
||||
presse: TheaterHomepagePresseFilter
|
||||
gallery: TheaterHomepageGalleryFilter
|
||||
tickets: TheaterHomepageTicketsFilter
|
||||
}
|
||||
|
||||
type TheaterHomepageConnectionEdges {
|
||||
cursor: String!
|
||||
node: TheaterHomepage
|
||||
}
|
||||
|
||||
type TheaterHomepageConnection implements Connection {
|
||||
pageInfo: PageInfo!
|
||||
totalCount: Float!
|
||||
edges: [TheaterHomepageConnectionEdges]
|
||||
}
|
||||
|
||||
type Mutation {
|
||||
addPendingDocument(collection: String!, relativePath: String!, template: String): DocumentNode!
|
||||
updateDocument(collection: String, relativePath: String!, params: DocumentUpdateMutation!): DocumentNode!
|
||||
deleteDocument(collection: String, relativePath: String!): DocumentNode!
|
||||
createDocument(collection: String, relativePath: String!, params: DocumentMutation!): DocumentNode!
|
||||
createFolder(collection: String, relativePath: String!): DocumentNode!
|
||||
updatePassword(password: String!): Boolean!
|
||||
updateUser(relativePath: String!, params: UserMutation!): User!
|
||||
createUser(relativePath: String!, params: UserMutation!): User!
|
||||
updatePost(relativePath: String!, params: PostMutation!): Post!
|
||||
createPost(relativePath: String!, params: PostMutation!): Post!
|
||||
updatePage(relativePath: String!, params: PageMutation!): Page!
|
||||
createPage(relativePath: String!, params: PageMutation!): Page!
|
||||
updateTheaterHomepage(relativePath: String!, params: TheaterHomepageMutation!): TheaterHomepage!
|
||||
createTheaterHomepage(relativePath: String!, params: TheaterHomepageMutation!): TheaterHomepage!
|
||||
}
|
||||
|
||||
input DocumentUpdateMutation {
|
||||
user: UserMutation
|
||||
post: PostMutation
|
||||
page: PageMutation
|
||||
theaterHomepage: TheaterHomepageMutation
|
||||
relativePath: String
|
||||
}
|
||||
|
||||
input DocumentMutation {
|
||||
user: UserMutation
|
||||
post: PostMutation
|
||||
page: PageMutation
|
||||
theaterHomepage: TheaterHomepageMutation
|
||||
}
|
||||
|
||||
input UserUsersPasswordMutation {
|
||||
value: String
|
||||
passwordChangeRequired: Boolean!
|
||||
}
|
||||
|
||||
input UserUsersMutation {
|
||||
username: String
|
||||
name: String
|
||||
email: String
|
||||
password: UserUsersPasswordMutation
|
||||
}
|
||||
|
||||
input UserMutation {
|
||||
users: [UserUsersMutation]
|
||||
}
|
||||
|
||||
input PostMutation {
|
||||
title: String
|
||||
date: String
|
||||
excerpt: String
|
||||
coverImage: String
|
||||
body: JSON
|
||||
}
|
||||
|
||||
input PageMutation {
|
||||
title: String
|
||||
description: String
|
||||
body: JSON
|
||||
}
|
||||
|
||||
input TheaterHomepageHeroMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
subtitle_de: String
|
||||
subtitle_en: String
|
||||
}
|
||||
|
||||
input TheaterHomepageProjektMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
}
|
||||
|
||||
input TheaterHomepageTeamOrganizationsMutation {
|
||||
name: String
|
||||
role_de: String
|
||||
role_en: String
|
||||
logo: String
|
||||
url: String
|
||||
}
|
||||
|
||||
input TheaterHomepageTeamMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
organizations: [TheaterHomepageTeamOrganizationsMutation]
|
||||
}
|
||||
|
||||
input TheaterHomepageStueckMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
}
|
||||
|
||||
input TheaterHomepageMitwirkungMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
director_name: String
|
||||
director_email: String
|
||||
director_phone: String
|
||||
}
|
||||
|
||||
input TheaterHomepageTermineEventsMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
date: String
|
||||
description_de: String
|
||||
description_en: String
|
||||
}
|
||||
|
||||
input TheaterHomepageTermineMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
events: [TheaterHomepageTermineEventsMutation]
|
||||
}
|
||||
|
||||
input TheaterHomepagePresseMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
}
|
||||
|
||||
input TheaterHomepageGalleryMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
images: [String]
|
||||
}
|
||||
|
||||
input TheaterHomepageTicketsMutation {
|
||||
title_de: String
|
||||
title_en: String
|
||||
content_de: JSON
|
||||
content_en: JSON
|
||||
ticket_url: String
|
||||
}
|
||||
|
||||
input TheaterHomepageMutation {
|
||||
title: String
|
||||
hero: TheaterHomepageHeroMutation
|
||||
projekt: TheaterHomepageProjektMutation
|
||||
team: TheaterHomepageTeamMutation
|
||||
stueck: TheaterHomepageStueckMutation
|
||||
mitwirkung: TheaterHomepageMitwirkungMutation
|
||||
termine: TheaterHomepageTermineMutation
|
||||
presse: TheaterHomepagePresseMutation
|
||||
gallery: TheaterHomepageGalleryMutation
|
||||
tickets: TheaterHomepageTicketsMutation
|
||||
}
|
||||
|
||||
schema {
|
||||
query: Query
|
||||
mutation: Mutation
|
||||
}
|
||||
Generated
-1
@@ -1 +0,0 @@
|
||||
[]
|
||||
Generated
-1345
File diff suppressed because it is too large
Load Diff
-492
@@ -1,492 +0,0 @@
|
||||
import { defineConfig } from "tinacms";
|
||||
import { UsernamePasswordAuthJSProvider, TinaUserCollection } from "tinacms-authjs/dist/tinacms";
|
||||
|
||||
export default defineConfig({
|
||||
contentApiUrlOverride: "/api/tina/gql",
|
||||
authProvider: new UsernamePasswordAuthJSProvider(),
|
||||
|
||||
build: {
|
||||
outputFolder: "admin",
|
||||
publicFolder: "public",
|
||||
},
|
||||
media: {
|
||||
tina: {
|
||||
mediaRoot: "images",
|
||||
publicFolder: "public",
|
||||
static: false,
|
||||
},
|
||||
},
|
||||
schema: {
|
||||
collections: [
|
||||
TinaUserCollection,
|
||||
{
|
||||
name: "post",
|
||||
label: "Blog Posts",
|
||||
path: "content/blog",
|
||||
format: "mdx",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Title",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "datetime",
|
||||
name: "date",
|
||||
label: "Publication Date",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "excerpt",
|
||||
label: "Excerpt",
|
||||
required: true,
|
||||
description: "Short summary of the blog post",
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "coverImage",
|
||||
label: "Cover Image",
|
||||
description: "Featured image for the blog post",
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "body",
|
||||
label: "Body",
|
||||
isBody: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
defaultItem: () => {
|
||||
return {
|
||||
title: "New Blog Post",
|
||||
date: new Date().toISOString(),
|
||||
excerpt: "",
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "page",
|
||||
label: "Pages",
|
||||
path: "content/pages",
|
||||
format: "mdx",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Title",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description",
|
||||
label: "Description",
|
||||
description: "Meta description for SEO",
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "body",
|
||||
label: "Content",
|
||||
isBody: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: "theaterHomepage",
|
||||
label: "Theater Homepage",
|
||||
path: "content/theater",
|
||||
format: "mdx",
|
||||
ui: {
|
||||
allowedActions: {
|
||||
create: false,
|
||||
delete: false,
|
||||
},
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title",
|
||||
label: "Page Title",
|
||||
isTitle: true,
|
||||
required: true,
|
||||
},
|
||||
// Hero Section
|
||||
{
|
||||
type: "object",
|
||||
name: "hero",
|
||||
label: "Hero Section",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "subtitle_de",
|
||||
label: "Subtitle (German)",
|
||||
required: true,
|
||||
ui: {
|
||||
component: "textarea",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "subtitle_en",
|
||||
label: "Subtitle (English)",
|
||||
required: true,
|
||||
ui: {
|
||||
component: "textarea",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 1: Das Projekt
|
||||
{
|
||||
type: "object",
|
||||
name: "projekt",
|
||||
label: "Section 1: Das Projekt / The Project",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 2: Wer steckt dahinter
|
||||
{
|
||||
type: "object",
|
||||
name: "team",
|
||||
label: "Section 2: Wer steckt dahinter / Who's Behind It",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
name: "organizations",
|
||||
label: "Organizations",
|
||||
list: true,
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "name",
|
||||
label: "Name",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "role_de",
|
||||
label: "Role (German)",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "role_en",
|
||||
label: "Role (English)",
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "logo",
|
||||
label: "Logo",
|
||||
description: "Organization logo image (SVG, PNG, JPG supported)",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "url",
|
||||
label: "Website URL",
|
||||
description: "Organization website link",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 3: Das Stück
|
||||
{
|
||||
type: "object",
|
||||
name: "stueck",
|
||||
label: "Section 3: Das Stück / The Play",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 4: Mitwirkung
|
||||
{
|
||||
type: "object",
|
||||
name: "mitwirkung",
|
||||
label: "Section 4: Mitwirkung / Participation",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_name",
|
||||
label: "Director Name",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_email",
|
||||
label: "Director Email",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "director_phone",
|
||||
label: "Director Phone",
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 5: Termine
|
||||
{
|
||||
type: "object",
|
||||
name: "termine",
|
||||
label: "Section 5: Termine / Schedule",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "object",
|
||||
name: "events",
|
||||
label: "Events",
|
||||
list: true,
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Event Title (German)",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Event Title (English)",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "date",
|
||||
label: "Date/Period",
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description_de",
|
||||
label: "Description (German)",
|
||||
ui: {
|
||||
component: "textarea",
|
||||
},
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "description_en",
|
||||
label: "Description (English)",
|
||||
ui: {
|
||||
component: "textarea",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 6: Presse
|
||||
{
|
||||
type: "object",
|
||||
name: "presse",
|
||||
label: "Section 6: Presse / Press",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 7: Foto Galerie
|
||||
{
|
||||
type: "object",
|
||||
name: "gallery",
|
||||
label: "Section 7: Foto Galerie / Photo Gallery",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "image",
|
||||
name: "images",
|
||||
label: "Gallery Images",
|
||||
list: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
// Section 8: Tickets
|
||||
{
|
||||
type: "object",
|
||||
name: "tickets",
|
||||
label: "Section 8: Tickets",
|
||||
fields: [
|
||||
{
|
||||
type: "string",
|
||||
name: "title_de",
|
||||
label: "Title (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "title_en",
|
||||
label: "Title (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_de",
|
||||
label: "Content (German)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "rich-text",
|
||||
name: "content_en",
|
||||
label: "Content (English)",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
type: "string",
|
||||
name: "ticket_url",
|
||||
label: "Ticket Purchase URL",
|
||||
description: "External link to ticket partner",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import { createDatabase, createLocalDatabase } from '@tinacms/datalayer'
|
||||
import { MongodbLevel } from 'mongodb-level'
|
||||
import { GitHubProvider } from 'tinacms-gitprovider-github'
|
||||
|
||||
const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'
|
||||
const branch = process.env.GITEA_BRANCH || 'main'
|
||||
|
||||
export default isLocal
|
||||
? createLocalDatabase()
|
||||
: createDatabase({
|
||||
gitProvider: new GitHubProvider({
|
||||
branch,
|
||||
owner: process.env.GITEA_OWNER!,
|
||||
repo: process.env.GITEA_REPO!,
|
||||
token: process.env.GITEA_TOKEN!,
|
||||
octokitOptions: {
|
||||
// Gitea's GitHub-compatible API
|
||||
baseUrl: 'https://git.idealconnected.com/api/v1',
|
||||
},
|
||||
}),
|
||||
databaseAdapter: new MongodbLevel({
|
||||
collectionName: 'tinacms',
|
||||
dbName: 'tinacms',
|
||||
mongoUri: process.env.MONGODB_URI!,
|
||||
}),
|
||||
})
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user