larpaso-website/admin/lib/schema.js
Lara 78ccad9c2d Reduce the readability section to the opening hours
The section showed the same information twice side by side - a dashed
"PDF" card labelled "So sieht es heute oft aus" against an hours table
labelled "So sieht es nachher aus". Reported as confusing; the file side
is dropped, the table stays.

The copy had to follow: it literally said "Dieselben Angaben, zweimal.
Einmal als Datei ... Einmal als Tabelle", which describes a comparison
that no longer exists. Rewritten to stand on its own, and the heading
"Lesbar schlägt vollständig" with it - it only made sense against the
file.

Also dropped the table's "TAG" header row. It had no counterpart on the
right and was left over from the two-column layout.

Removed throughout rather than hidden: the pdf-card/pdf-icon/pdf-name/
pdf-meta and readability-label/hours-head rules, the two-column grid
including its mobile override, and the four now-meaningless fields in
the admin schema.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-04 14:10:54 +02:00

349 lines
13 KiB
JavaScript
Raw 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: 'visual',
label: 'Google-Vorschau (rechts neben dem Hero-Text)',
type: 'group',
itemFields: [
{
key: 'customImage',
label: 'Eigenes Bild statt der Vorschau (leer = eingebaute Vorschau anzeigen)',
type: 'image',
},
{ key: 'label', label: 'Beschriftung oben', type: 'text' },
{ key: 'todayTag', label: 'Karte 1 – Tag-Label', type: 'text' },
{ key: 'todayUrl', label: 'Karte 1 – URL-Zeile', type: 'text' },
{ key: 'todayTitle', label: 'Karte 1 – Titel', type: 'text' },
{ key: 'todayDesc', label: 'Karte 1 – Beschreibung', type: 'textarea' },
{ key: 'dividerText', label: 'Trenntext zwischen den Karten', type: 'text' },
{ key: 'possibleTag', label: 'Karte 2 – Tag-Label', type: 'text' },
{ key: 'possibleUrl', label: 'Karte 2 – URL-Zeile', type: 'text' },
{ key: 'possibleTitle', label: 'Karte 2 – Titel', type: 'text' },
{ key: 'possibleDesc', label: 'Karte 2 – Beschreibung', type: 'richtext' },
{ key: 'possibleMeta', label: 'Karte 2 – Bewertungszeile', type: 'text' },
],
},
],
},
{
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: 'kategorie', label: 'Kategorie', type: 'text' },
{ key: 'titel', label: 'Titel / Name', type: 'text' },
{ key: 'text', label: '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 = Platzhalter)', type: 'text' },
{ key: 'hours', label: 'Erreichbarkeit (leer = Platzhalter)', type: 'text' },
{ key: 'email', label: 'E-Mail-Adresse', type: 'email' },
{ key: 'howWeWork', label: '"Wie wir arbeiten" (leer = Platzhalter)', 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' },
],
},
];