- Next.js 16 App Router site with the ANOUMA design system - Payload CMS (PostgreSQL) for offers, events, posts and page content - WebRTC video-call system with custom signaling server - SMTP email reminders and booking-request notifications - Customer accounts, calendar-based availability, and booking workflow
242 lines
8.6 KiB
TypeScript
242 lines
8.6 KiB
TypeScript
/**
|
|
* 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);
|
|
});
|