larpaso-website/admin/lib/schema.js
Lara 275e479aba SEO groundwork; hide the empty 'Wie wir arbeiten' block
- robots.txt and sitemap.xml (both answered with the homepage before)
- real 404 page; nginx returns 404 instead of the homepage for unknown paths
- gzip and caching for fonts, images, videos
- JSON-LD as one graph: ProfessionalService (+founders, offer), WebSite,
  FAQPage built from the FAQ answers
- sharper title and a meta description under 160 characters,
  real descriptions for imprint and privacy pages
- imprint cites § 5 DDG (TMG was replaced in May 2024)
- empty kontakt.howWeWork hides the block instead of a red placeholder

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 11:13:06 +02:00

346 lines
13 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const { FONT_NAMES } = require('./fonts');
/**
* Describes the editable shape of content/site.json (file: 'content', the
* default) and content/theme.json (file: 'theme') for the admin UI.
*
* Field types: text, textarea, richtext, email, boolean, image,
* list-string, list-richtext, list-object, group, select, size, color.
* "richtext"/"list-richtext" fields are edited with a WYSIWYG toolbar
* (bold/italic/underline + per-selection font) and stored as a small
* allowlisted HTML subset - see admin/lib/rich-sanitize.js for what's
* allowed and src/lib/content.ts for how it's rendered on the site.
* "group" is a single nested object (not repeatable) described by itemFields.
* "color" fields store { r, g, b, a }. "size" fields store a CSS length (e.g. "1.25rem").
*/
module.exports = [
{
key: 'meta',
label: 'SEO / Meta',
fields: [{ key: 'description', label: 'Meta-Beschreibung', type: 'textarea' }],
},
{
key: 'nav',
label: 'Navigation',
fields: [
{ key: 'logo', label: 'Logo', type: 'image' },
{ key: 'logoAlt', label: 'Logo Alt-Text', type: 'text' },
{ key: 'ctaLabel', label: 'Button-Text', type: 'text' },
{
key: 'links',
label: 'Menüpunkte',
type: 'list-object',
itemLabel: 'Menüpunkt',
itemFields: [
{ key: 'label', label: 'Text', type: 'text' },
{ key: 'anchor', label: 'Sprungziel (Abschnitt-ID, ohne #)', type: 'text' },
],
},
],
},
{
key: 'hero',
label: 'Hero (ganz oben)',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'titleBefore', label: 'Titel – Teil 1', type: 'text' },
{ key: 'titleEmphasis', label: 'Titel – hervorgehobenes Wort', type: 'text' },
{ key: 'titleAfter', label: 'Titel – Teil 2', type: 'text' },
{ key: 'lead', label: 'Einleitungstext', type: 'richtext' },
{ key: 'ctaLabel', label: 'Button-Text', type: 'text' },
{ key: 'secondaryLabel', label: 'Zweiter Button (zur Mappe), leer = ausblenden', type: 'text' },
{
key: 'visual',
label: 'Geräte-Ansicht (rechts neben dem Hero-Text)',
type: 'group',
itemFields: [
{
key: 'customImage',
label: 'Eigenes Bild statt Laptop und Handy (leer = Geräte anzeigen)',
type: 'image',
},
{ key: 'laptop', label: 'Bild im Laptop (Querformat 16:9)', type: 'image' },
{ key: 'phone', label: 'Bild im Handy (Hochformat)', type: 'image' },
],
},
],
},
{
key: 'warum',
label: 'Abschnitt 02 – Warum uns',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'lead', label: 'Text', type: 'richtext' },
{
key: 'readability',
label: 'PDF-vs-Tabelle-Vergleich',
type: 'group',
itemFields: [
{
key: 'customImage',
label: 'Eigenes Bild statt der Tabelle (leer = Öffnungszeiten-Tabelle anzeigen)',
type: 'image',
},
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'lead', label: 'Text', type: 'richtext' },
{ key: 'hoursTitle', label: 'Titel der Tabelle', type: 'text' },
{
key: 'hoursRows',
label: 'Tabellenzeilen',
type: 'list-object',
itemLabel: 'Zeile',
itemFields: [
{ key: 'day', label: 'Tag', type: 'text' },
{ key: 'time', label: 'Uhrzeit', type: 'text' },
],
},
],
},
],
},
{
key: 'preis',
label: 'Abschnitt 01 – Preis',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'amount', label: 'Preis', type: 'text' },
{ key: 'amountNote', label: 'Preis-Zusatz', type: 'text' },
{ key: 'lead', label: 'Einleitungstext', type: 'richtext' },
{ key: 'slotsNote', label: 'Plätze-Hinweis', type: 'text' },
{
key: 'features',
label: 'Leistungspunkte',
type: 'list-object',
itemLabel: 'Punkt',
itemFields: [
{ key: 'text', label: 'Text', type: 'text' },
{ key: 'excluded', label: 'Als "nicht enthalten" markieren', type: 'boolean' },
],
},
{ key: 'asideTitle', label: 'Kasten-Überschrift', type: 'text' },
{ key: 'asideParagraph1', label: 'Kasten-Absatz 1', type: 'richtext' },
{ key: 'asideParagraph2', label: 'Kasten-Absatz 2', type: 'richtext' },
{ key: 'ctaLabel', label: 'Button-Text', type: 'text' },
],
},
{
key: 'highlights',
label: 'Abschnitt 02 – Die drei Punkte',
type: 'array-root',
itemLabel: 'Punkt',
itemFields: [
{ key: 'num', label: 'Kennung (z. B. I, II, III)', type: 'text' },
{ key: 'title', label: 'Titel', type: 'text' },
{ key: 'text', label: 'Text', type: 'textarea' },
],
},
{
key: 'leistungen',
label: 'Abschnitt 03 – Leistungen',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'lead', label: 'Text', type: 'richtext' },
{
key: 'items',
label: 'Leistungen',
type: 'list-object',
itemLabel: 'Leistung',
itemFields: [
{ key: 'num', label: 'Nummer', type: 'text' },
{ key: 'title', label: 'Titel', type: 'text' },
{ key: 'text', label: 'Text', type: 'textarea' },
],
},
{ key: 'nichtMachenTitle', label: '"Was wir nicht machen" – Überschrift', type: 'text' },
{ key: 'nichtMachen', label: '"Was wir nicht machen" – Punkte', type: 'list-string' },
],
},
{
key: 'mappe',
label: 'Abschnitt 04 – Mappe',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{
key: 'items',
label: 'Arbeiten',
type: 'list-object',
itemLabel: 'Arbeit',
itemFields: [
{ key: 'image', label: 'Bildschirmfoto', type: 'image' },
{ key: 'mobileImage', label: 'Handy-Ansicht (Hochformat, leer = kein Handy)', type: 'image' },
{ key: 'video', label: 'Video im Laptop (Pfad zu einer .mp4, leer = Standbild)', type: 'text' },
{ key: 'fullImage', label: 'Ganze Seite, Computer (Ordner mit Streifen, z. B. /mappe/ganz/name/; leer = nicht vergrößerbar)', type: 'text' },
{ key: 'fullMobileImage', label: 'Ganze Seite, Handy (Ordner mit Streifen, z. B. /mappe/ganz/name-mobil/)', type: 'text' },
{ key: 'kategorie', label: 'Kategorie', type: 'text' },
{ key: 'titel', label: 'Titel / Name', type: 'text' },
{ key: 'aufgabe', label: 'Die Aufgabe (leer = nur Text zeigen)', type: 'textarea' },
{ key: 'text', label: 'Unsere Lösung / Text', type: 'textarea' },
{ key: 'nameHidden', label: 'Name noch verstecken (Platzhalter zeigen)', type: 'boolean' },
],
},
],
},
{
key: 'ablauf',
label: 'Abschnitt 05 – Ablauf',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{
key: 'steps',
label: 'Schritte',
type: 'list-object',
itemLabel: 'Schritt',
itemFields: [
{ key: 'num', label: 'Nummer', type: 'text' },
{ key: 'title', label: 'Titel', type: 'text' },
{ key: 'text', label: 'Text', type: 'textarea' },
{ key: 'dauer', label: 'Dauer (leer = wird nicht angezeigt)', type: 'text' },
],
},
],
},
{
key: 'about',
label: 'Abschnitt 06 – Über uns',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'quote', label: 'Zitat', type: 'text' },
{ key: 'paragraphs', label: 'Absätze', type: 'list-richtext' },
{ key: 'photo', label: 'Foto', type: 'image' },
],
},
{
key: 'faq',
label: 'Abschnitt 07 – FAQ',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{
key: 'items',
label: 'Fragen',
type: 'list-object',
itemLabel: 'Frage',
itemFields: [
{ key: 'question', label: 'Frage', type: 'text' },
{ key: 'answer', label: 'Antwort (leer = Platzhalter)', type: 'textarea' },
],
},
],
},
{
key: 'kontakt',
label: 'Abschnitt 08 – Kontakt',
fields: [
{ key: 'eyebrow', label: 'Kicker-Zeile', type: 'text' },
{ key: 'title', label: 'Überschrift', type: 'text' },
{ key: 'lead', label: 'Einleitungstext', type: 'richtext' },
{ key: 'phone', label: 'Telefonnummer (leer = ausgeblendet, auch im Impressum)', type: 'text' },
{ key: 'hours', label: 'Telefonische Erreichbarkeit unter der Nummer (leer = ausgeblendet)', type: 'text' },
{ key: 'email', label: 'E-Mail-Adresse', type: 'email' },
{ key: 'howWeWork', label: '"Wie wir arbeiten" (leer = ausgeblendet)', type: 'textarea' },
],
},
{
key: 'footer',
label: 'Footer',
fields: [
{ key: 'laraNachname', label: 'Lara – Nachname (leer = Platzhalter)', type: 'text' },
{ key: 'pascalNachname', label: 'Pascal – Nachname (leer = Platzhalter)', type: 'text' },
],
},
{
key: 'impressum',
label: 'Impressum',
fields: [
{ key: 'anbieterAngaben', label: 'Name/Firma + Anschrift (leer = Platzhalter)', type: 'textarea' },
{ key: 'ustId', label: 'Umsatzsteuer-ID (leer = Platzhalter)', type: 'textarea' },
{ key: 'verantwortlich', label: 'Verantwortlich für den Inhalt (leer = Platzhalter)', type: 'textarea' },
{ key: 'streitschlichtung', label: 'Verbraucherstreitbeilegung (§ 36 VSBG, leer = Platzhalter)', type: 'textarea' },
{
key: 'haftungSections',
label: 'Haftungsausschluss – Abschnitte',
type: 'list-object',
itemLabel: 'Abschnitt',
itemFields: [
{ key: 'heading', label: 'Überschrift', type: 'text' },
{ key: 'text', label: 'Text', type: 'richtext' },
],
},
{ key: 'note', label: 'Hinweistext am Seitenende', type: 'textarea' },
],
},
{
key: 'datenschutz',
label: 'Datenschutz',
fields: [
{ key: 'intro', label: 'Einleitung (leer = Platzhalter)', type: 'textarea' },
{
key: 'sections',
label: 'Abschnitte',
type: 'list-object',
itemLabel: 'Abschnitt',
itemFields: [
{ key: 'heading', label: 'Überschrift', type: 'text' },
{
key: 'text',
label: 'Text (leer = Platzhalter; {email} wird durch die Kontakt-E-Mail ersetzt)',
type: 'richtext',
},
],
},
{ key: 'note', label: 'Hinweistext am Seitenende', type: 'textarea' },
],
},
{
key: 'fonts',
file: 'theme',
label: 'Design – Schriftarten',
fields: [
{ key: 'heading', label: 'Überschriften-Schrift', type: 'select', options: FONT_NAMES },
{ key: 'body', label: 'Fließtext-Schrift', type: 'select', options: FONT_NAMES },
],
},
{
key: 'fontSizes',
file: 'theme',
label: 'Design – Schriftgrößen',
fields: [
{ key: 'sm', label: 'Klein (Kicker-Zeilen, Feinschrift)', type: 'size', min: 0.6, max: 1.5, step: 0.025 },
{ key: 'base', label: 'Standard (Fließtext)', type: 'size', min: 0.75, max: 1.5, step: 0.025 },
{ key: 'md', label: 'Mittel (Einleitungstexte)', type: 'size', min: 0.9, max: 1.75, step: 0.025 },
{ key: 'lg', label: 'Groß (H3)', type: 'size', min: 1, max: 2.5, step: 0.025 },
{ key: 'xl', label: 'Größer (H2 mobil)', type: 'size', min: 1.2, max: 3, step: 0.025 },
{ key: '2xl', label: 'Sehr groß (H2)', type: 'size', min: 1.5, max: 4, step: 0.025 },
{ key: '3xl', label: 'Riesig (H1)', type: 'size', min: 1.75, max: 5, step: 0.025 },
],
},
{
key: 'colors',
file: 'theme',
label: 'Design – Farben',
fields: [
{ key: 'bg', label: 'Hintergrund', type: 'color' },
{ key: 'bgAlt', label: 'Hintergrund (Variante)', type: 'color' },
{ key: 'surface', label: 'Flächen / Karten', type: 'color' },
{ key: 'text', label: 'Text', type: 'color' },
{ key: 'textMuted', label: 'Text (gedämpft)', type: 'color' },
{ key: 'border', label: 'Rahmen', type: 'color' },
{ key: 'primary', label: 'Primärfarbe (Buttons, Links)', type: 'color' },
{ key: 'primaryDark', label: 'Primärfarbe (Hover)', type: 'color' },
{ key: 'primaryContrast', label: 'Text auf Primärfarbe', type: 'color' },
{ key: 'accent', label: 'Akzentfarbe', type: 'color' },
{ key: 'accentDark', label: 'Akzentfarbe (dunkel)', type: 'color' },
{ key: 'success', label: 'Erfolg', type: 'color' },
{ key: 'danger', label: 'Fehler/Warnung', type: 'color' },
{ key: 'flag', label: 'Platzhalter-Markierung', type: 'color' },
{ key: 'flagBg', label: 'Platzhalter-Markierung (Hintergrund)', type: 'color' },
{ key: 'googleBlue', label: 'Linkfarbe in der Google-Vorschau', type: 'color' },
],
},
];