Files
anouma/scripts/seed.ts
T
maro 526a908c80
CI / test (push) Successful in 3m42s
Release / test (push) Successful in 3m44s
Release / build-and-deploy (push) Successful in 8m59s
Redesign: warmer, more editorial visual pass; remove stray em dashes
Replaces em dashes with en dashes across all hardcoded copy, emails, and
seed content, then reworks the site's visual language so it reads as
deliberately designed rather than assembled from generic components:

- New ArrowLink component replaces ad hoc "Mehr erfahren ->" links and
  ghost buttons; one shared FormField (TextField/TextAreaField) replaces
  7 near-duplicate field styles across every form
- Navbar loses its glassmorphism blur for a solid warm background, gets a
  subtle active-link underline and a softer, warmer mega-menu
- PageHeader supports per-page blob tone/position and a compact mode
  instead of an identical band on every interior page
- Homepage: the core offer is featured large instead of four equal cards,
  the philosophy section gets a single calmer accent blob, blog teasers
  now show their cover image like the real /aktuelles page does
- Booking calendar and widget: 44px touch targets throughout (month nav,
  day cells, time slots, cancel action), a visible "today" ring, and
  responsive padding so the calendar isn't cramped on small screens
- Quieter, warmer empty states on /termine and /aktuelles

No functional changes: all interaction logic (nav menus, forms, booking,
CMS data fetching) is untouched, only markup and styling.
2026-09-06 22:00:28 +02:00

242 lines
8.6 KiB
TypeScript
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.
/**
* Seeds the CMS with the original ANOUMA content (the 7 offers from
* texte.txt, plus starting text for every editable page). Safe to re-run —
* offers are matched and updated by slug, globals are simply overwritten.
*
* Usage: npm run seed (requires DATABASE_URI + PAYLOAD_SECRET in .env)
*
* Deliberately does NOT create a Users document — the first admin account is
* created through Payload's own "create first user" screen at /admin, so no
* password is ever hardcoded here.
*/
import { getPayload } from "payload";
import config from "../payload.config";
import {
prozessbegleitung,
doula,
erdenkinder,
maedchenkreis,
singenImKreis,
singenFuerSchwangere,
mamaBabySingkreis,
ueberMich,
} from "../lib/texte";
import { lexicalFromParagraphs, lexicalFromSections } from "./lexical";
async function seedOffers(payload: Awaited<ReturnType<typeof getPayload>>) {
const offers = [
{
slug: "prozessbegleitung",
title: "Prozessbegleitung",
category: "prozessbegleitung" as const,
order: 1,
shortDescription: "Persönliche Begleitung in Lebensphasen, Veränderungen und inneren Prozessen.",
description: lexicalFromSections(prozessbegleitung.intro, prozessbegleitung.sections),
},
{
slug: "doula-begleitung",
title: "Doula-Begleitung",
category: "doula-begleitung" as const,
order: 2,
shortDescription: "Begleitung vor, während und nach der Geburt.",
description: lexicalFromSections(doula.intro, doula.sections),
},
{
slug: "erdenkinder",
title: "Erdenkinder",
category: "kindergruppen" as const,
order: 3,
shortDescription: "Naturverbunden. Kreativ. Frei. Für Kinder.",
description: lexicalFromParagraphs(erdenkinder.paragraphs),
},
{
slug: "maedchenkreis",
title: "Mädchenkreis",
category: "kindergruppen" as const,
order: 4,
shortDescription: "Stärkung. Vertrauen. Gemeinschaft. Für Mädchen.",
description: lexicalFromParagraphs(maedchenkreis.paragraphs),
},
{
slug: "singen-im-kreis",
title: "Singen im Kreis",
category: "singkreise" as const,
order: 5,
shortDescription: "Gemeinsam singen. Verbunden sein.",
description: lexicalFromParagraphs(singenImKreis.paragraphs),
},
{
slug: "singen-fuer-schwangere",
title: "Singen für Schwangere",
category: "singkreise" as const,
order: 6,
shortDescription: "Lieder, die tragen. Für dich und dein Baby.",
description: lexicalFromParagraphs(singenFuerSchwangere.paragraphs),
},
{
slug: "mama-baby-singkreis",
title: "Singen für Mamas mit Baby",
category: "singkreise" as const,
order: 7,
shortDescription: "Zeit für dich und dein Baby. Singen. Austauschen. Auftanken.",
description: lexicalFromParagraphs(mamaBabySingkreis.paragraphs),
},
{
// A private, bookable single-session offer — not listed in the menu
// or on /angebote (see `visibility`), reachable only via its direct
// link or a booking Anna creates herself. Content reuses the
// "Individuelle Begleitung" section of Prozessbegleitung, which is
// exactly this kind of 1:1 session in the original texte.txt.
slug: "einzelbegleitung",
title: "Einzelbegleitung",
category: "prozessbegleitung" as const,
order: 8,
visibility: "private" as const,
bookable: true,
price: "auf Anfrage",
durationMinutes: 60,
shortDescription: "Persönliche Einzelbegleitung – vor Ort oder online.",
description: lexicalFromParagraphs(prozessbegleitung.sections[0].paragraphs),
},
];
for (const entry of offers) {
const data = { visibility: "public" as const, ...entry };
const existing = await payload.find({
collection: "offers",
where: { slug: { equals: data.slug } },
limit: 1,
});
if (existing.docs[0]) {
await payload.update({ collection: "offers", id: existing.docs[0].id, data, draft: false });
console.log(` ↺ Angebot aktualisiert: ${data.title}`);
} else {
await payload.create({ collection: "offers", data, draft: false });
console.log(` + Angebot angelegt: ${data.title}`);
}
}
}
async function seedGlobals(payload: Awaited<ReturnType<typeof getPayload>>) {
await payload.updateGlobal({
slug: "home",
data: {
heroEyebrow: "Prozessbegleitung · Doula · Gemeinschaft",
heroTitle: "Willkommen bei Anouma",
heroHighlight: ueberMich.closing.highlight,
heroSupporting: ueberMich.closing.paragraph,
aboutEyebrow: "Über mich",
aboutTitle: "Mein Weg, meine Werte und was mich bewegt",
aboutText: lexicalFromParagraphs(ueberMich.paragraphs.slice(0, 2)),
valuesEyebrow: "Philosophie",
valuesTitle: "Räume, in denen wir uns erinnern dürfen, wer wir sind",
values: [
{
title: "Vertrauen",
quote: "Alles, was wir für unseren Weg brauchen, ist bereits in uns angelegt.",
},
{
title: "Verbindung",
quote: "Räume für Verbindung – mit uns selbst, miteinander und mit der Natur.",
},
{ title: "Natur", quote: "Wir sind nicht nur in der Natur – wir sind ein Teil von ihr." },
],
ctaTitle: "Kennenlernen & Termine vereinbaren",
ctaLead:
"Ich freue mich, von dir zu hören – schreib mir oder buche direkt ein unverbindliches Kennenlerngespräch.",
},
});
console.log(" ↺ Global „Startseite“ gesetzt");
await payload.updateGlobal({
slug: "about",
data: {
eyebrow: "Über mich",
title: "Mein Weg, meine Werte und was mich bewegt",
body: lexicalFromParagraphs(ueberMich.paragraphs),
closingLead: ueberMich.closing.lead,
closingHighlight: ueberMich.closing.highlight,
closingParagraph: ueberMich.closing.paragraph,
},
});
console.log(" ↺ Global „Über mich“ gesetzt");
await payload.updateGlobal({
slug: "angebote-intro",
data: {
title: "Angebote",
lead: "Jeder Raum ist anders – und darf so genau das sein, was gerade gebraucht wird. Ein Überblick über meine Begleitung für Erwachsene, Kinder und Gemeinschaft.",
},
});
console.log(" ↺ Global „Angebote (Einleitung)“ gesetzt");
await payload.updateGlobal({
slug: "aktuelles-intro",
data: {
eyebrow: "Aktuelles",
title: "Neuigkeiten, Termine und Inspiration",
lead: "Hier findest du künftig aktuelle Termine, Ankündigungen und Impulse rund um die Angebote von Anouma.",
},
});
console.log(" ↺ Global „Aktuelles (Einleitung)“ gesetzt");
await payload.updateGlobal({
slug: "contact",
data: {
eyebrow: "Kontakt",
title: "Ich freue mich, von dir zu hören",
lead: "Ob Frage, Anliegen oder der Wunsch nach einem Kennenlerngespräch – schreib mir gerne.",
// Placeholder contact details — replace with the real ones in the admin.
email: "hallo@anouma.org",
phone: "+49 000 00 00 00",
region: "Platzhalter – Ort / Region",
},
});
console.log(" ↺ Global „Kontakt“ gesetzt (mit Platzhalter-Kontaktdaten — bitte im Admin ersetzen)");
await payload.updateGlobal({
slug: "booking",
data: {
eyebrow: "Termin buchen",
title: "Kennenlernen & Termine vereinbaren",
lead: "Der erste Schritt ist oft der wichtigste. Melde dich gerne – unverbindlich und in deinem Tempo.",
steps: [
{
title: "Nachricht schreiben",
text: "Schildere kurz dein Anliegen und welches Angebot dich interessiert – über das Formular unten oder direkt per E-Mail.",
},
{
title: "Kennenlernen",
text: "In einem ersten, unverbindlichen Gespräch schauen wir gemeinsam, was du gerade brauchst und welcher Weg passt.",
},
{
title: "Termin vereinbaren",
text: "Passt es für euch beide, vereinbaren wir gemeinsam die weiteren Termine.",
},
],
formNote:
"Aktuell öffnet dieses Formular dein E-Mail-Programm. Sobald ein Buchungstool (z. B. für Online-Terminvergabe) ausgewählt ist, kann es hier eingebunden werden.",
},
});
console.log(" ↺ Global „Termin buchen“ gesetzt");
}
async function run() {
const payload = await getPayload({ config });
console.log("Angebote:");
await seedOffers(payload);
console.log("Seiteninhalte:");
await seedGlobals(payload);
console.log("\nFertig. Richte jetzt unter /admin den ersten Admin-Zugang ein (dort wird kein Passwort vorgegeben).");
process.exit(0);
}
run().catch((err) => {
console.error(err);
process.exit(1);
});