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 des Vergleichs (leer = eingebauten Vergleich anzeigen)', type: 'image', }, { key: 'title', label: 'Überschrift', type: 'text' }, { key: 'lead', label: 'Text', type: 'richtext' }, { key: 'beforeLabel', label: 'Label links ("vorher")', type: 'text' }, { key: 'pdfName', label: 'Dateiname im PDF-Beispiel', type: 'text' }, { key: 'pdfMeta', label: 'Zusatzzeile im PDF-Beispiel', type: 'text' }, { key: 'afterLabel', label: 'Label rechts ("nachher")', type: 'text' }, { 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 = Platzhalter)', 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' }, ], }, ];