cms.c2sgmbh/scripts/seed-blogwoman-continue.ts
Martin Porwoll 15f3fa2481 feat(BlogWoman): add tenant seed scripts and block migrations
- Add migration for BlogWoman page blocks (favorites-block, series-block,
  series-detail-block, featured-content-block) with all required columns
- Add seed scripts for BlogWoman tenant creation with full content:
  - 10 pages (Startseite, Über mich, Newsletter, etc.)
  - 7 blog posts
  - 9 series (GRFI, Investment-Piece, Pleasure P&L, etc.)
  - 4 categories, 10 tags, 1 author
  - Navigation, social links, cookie configuration
- Add Konzept-KI guide for AI-assisted tenant creation
- Add BlogWoman tenant prompt template

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 11:13:32 +00:00

1012 lines
34 KiB
TypeScript
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.

/**
* BlogWoman Tenant Seed - Continuation
*
* Continues seeding after the initial run failed on Favorites.
* Creates Pages, Posts, Navigation, Social Links, and Cookie Config.
*
* Run with: npx tsx scripts/seed-blogwoman-continue.ts
*/
import { getPayload } from 'payload'
import config from '../src/payload.config'
// Helper to create Lexical Rich Text content
function createRichText(content: string | string[]): object {
const paragraphs = Array.isArray(content) ? content : [content]
return {
root: {
type: 'root',
children: paragraphs.map((text) => ({
type: 'paragraph',
children: [{ type: 'text', text }],
})),
direction: 'ltr',
format: '',
indent: 0,
version: 1,
},
}
}
function createRichTextWithHeading(heading: string, paragraphs: string[]): object {
return {
root: {
type: 'root',
children: [
{
type: 'heading',
tag: 'h2',
children: [{ type: 'text', text: heading }],
},
...paragraphs.map((text) => ({
type: 'paragraph',
children: [{ type: 'text', text }],
})),
],
direction: 'ltr',
format: '',
indent: 0,
version: 1,
},
}
}
function createRichTextWithBullets(heading: string, intro: string, bullets: string[]): object {
return {
root: {
type: 'root',
children: [
{
type: 'heading',
tag: 'h2',
children: [{ type: 'text', text: heading }],
},
{
type: 'paragraph',
children: [{ type: 'text', text: intro }],
},
{
type: 'list',
listType: 'bullet',
children: bullets.map((b) => ({
type: 'listitem',
children: [{ type: 'text', text: b }],
})),
},
],
direction: 'ltr',
format: '',
indent: 0,
version: 1,
},
}
}
async function seed() {
console.log('🚀 Continuing BlogWoman Tenant Seed...\n')
const payload = await getPayload({ config })
// Get existing tenant
const existingTenant = await payload.find({
collection: 'tenants',
where: { slug: { equals: 'blogwoman' } },
})
if (existingTenant.docs.length === 0) {
console.error('❌ Tenant "blogwoman" not found! Run seed-blogwoman.ts first.')
process.exit(1)
}
const tenantId = existingTenant.docs[0].id as number
console.log(`✓ Found tenant "BlogWoman" (ID: ${tenantId})`)
// Get existing category IDs
const categories = await payload.find({
collection: 'categories',
where: { tenant: { equals: tenantId } },
})
const categoryIds: Record<string, number> = {}
for (const cat of categories.docs) {
categoryIds[cat.slug] = cat.id as number
}
console.log(`✓ Found ${Object.keys(categoryIds).length} categories`)
// Get existing tag IDs
const tags = await payload.find({
collection: 'tags',
where: { tenant: { equals: tenantId } },
})
const tagIds: Record<string, number> = {}
for (const tag of tags.docs) {
tagIds[tag.slug] = tag.id as number
}
console.log(`✓ Found ${Object.keys(tagIds).length} tags`)
// Get author ID
const authors = await payload.find({
collection: 'authors',
where: {
and: [
{ slug: { equals: 'dr-caroline-porwoll' } },
{ tenant: { equals: tenantId } }
]
},
})
const authorId = authors.docs[0]?.id as number
console.log(`✓ Found author (ID: ${authorId})`)
// ============================================
// 1. CREATE PAGES
// ============================================
console.log('\n--- 1. Creating Pages ---')
const pages = [
// HOME PAGE
{
title: 'Startseite',
slug: 'home',
status: 'published',
hero: {
headline: 'BLOGWOMAN',
subline: 'Für Frauen, die Karriere, Familie & Stil ernst nehmen.',
},
layout: [
{
blockType: 'hero-block',
headline: 'BLOGWOMAN',
subline: 'Für Frauen, die Karriere, Familie & Stil ernst nehmen.',
alignment: 'center',
overlay: true,
overlayOpacity: 0.4,
cta: {
text: 'Newsletter',
link: '/newsletter',
style: 'primary',
},
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Dr. Caroline Porwoll', [
'Unternehmerin. Mutter. Systemdenkerin.',
'Ich glaube nicht an Work-Life-Balance ich glaube an Systeme, die beides möglich machen.',
]),
},
{
blockType: 'posts-list-block',
headline: 'Aus dem Blog',
limit: 3,
showPagination: false,
},
{
blockType: 'newsletter-block',
headline: 'Der BlogWoman Brief',
description: 'Jeden Dienstag in deinem Postfach.',
buttonText: 'ANMELDEN',
privacyText: 'Kein Spam. Jederzeit abmelden.',
},
],
seo: {
metaTitle: 'BlogWoman Karriere, Familie & Stil mit System',
metaDescription:
'Für Frauen, die Karriere, Familie und Stil ernst nehmen. Systeme statt Motivation. Von Dr. Caroline Porwoll.',
},
},
// ABOUT PAGE
{
title: 'Über mich',
slug: 'caroline',
status: 'published',
hero: {
headline: 'Dr. Caroline Porwoll',
subline: 'Unternehmerin. Mutter. Die Frau hinter BlogWoman.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Dr. Caroline Porwoll',
subline: 'Unternehmerin. Mutter. Die Frau hinter BlogWoman.',
alignment: 'center',
overlay: true,
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('In 30 Sekunden:', [
'• Promovierte Wirtschaftswissenschaftlerin',
'• Gründerin & Geschäftsführerin der Complex Care Solutions GmbH',
'• Mutter',
'• 15+ Jahre Erfahrung in Unternehmensführung',
'• Entwicklerin von Systemen, die Karriere und Familie verbinden',
'',
'BlogWoman ist das, was ich mir selbst gewünscht hätte als ich versuchte, alles unter einen Hut zu bekommen.',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Es gab diesen Moment', [
'Ich stand vor dem Kleiderschrank, hatte ein wichtiges Board-Meeting in 45 Minuten, ein Kind mit Fieber zuhause, und absolut keine Ahnung, was ich anziehen sollte.',
'Nicht weil ich nichts hatte. Sondern weil ich kein System hatte.',
'In diesem Moment wurde mir klar: Ich brauche keine Motivation. Ich brauche keine Inspiration. Ich brauche Strukturen, die funktionieren auch wenn ich nicht funktioniere.',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Wofür ich stehe', [
'→ System schlägt Motivation Willenskraft ist endlich. Gute Systeme nicht.',
'→ Investment statt Konsum Ob Blazer oder Spa-Tag: Ich rechne den ROI.',
'→ Energie ist Strategie Regeneration ist keine Belohnung. Sie ist die Voraussetzung.',
'→ Weniger, aber besser Qualität über Quantität. In allem.',
'→ Ehrlichkeit über Perfektion Ich zeige, was funktioniert und was nicht.',
]),
},
{
blockType: 'cta-block',
headline: 'Lass uns verbunden bleiben',
backgroundColor: 'dark',
buttons: [
{ text: 'YouTube abonnieren', link: 'https://youtube.com/@blogwoman', style: 'primary' },
{ text: 'Newsletter anmelden', link: '/newsletter', style: 'secondary' },
],
},
],
seo: {
metaTitle: 'Über Caroline | BlogWoman',
metaDescription:
'Dr. Caroline Porwoll Unternehmerin, Mutter, Gründerin von BlogWoman. Die Geschichte hinter den Systemen.',
},
},
// NEWSLETTER PAGE
{
title: 'Newsletter',
slug: 'newsletter',
status: 'published',
hero: {
headline: 'Der BlogWoman Brief',
subline: 'Performance & Pleasure für Karriere, Familie & Stil.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Der BlogWoman Brief',
subline: 'Performance & Pleasure für Karriere, Familie & Stil. Jeden Dienstag direkt in dein Postfach.',
alignment: 'center',
},
{
blockType: 'newsletter-block',
headline: 'Jetzt anmelden',
description:
'☑ GRFI-Checkliste (PDF) ☑ 30-Teile Capsule Wardrobe Liste ☑ Pleasure P&L Template',
buttonText: 'ANMELDEN',
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Jeden Dienstag in deinem Postfach:', [
'→ The Impact Move 1 Mini-Tipp, sofort umsetzbar. Keine Theorie.',
'→ Video Recap Was diese Woche auf YouTube lief falls du\'s verpasst hast.',
'→ P&L Pick 1 Produkt-Empfehlung mit meiner ehrlichen Rechnung.',
'→ Backstage Note 3 Sätze aus meinem echten Leben. Kein Hochglanz.',
]),
},
],
seo: {
metaTitle: 'Der BlogWoman Brief | Newsletter',
metaDescription:
'Performance & Pleasure für Karriere, Familie & Stil. Jeden Dienstag direkt in dein Postfach. Plus: Kostenlose Downloads.',
},
},
// FAVORITEN PAGE
{
title: 'Favoriten',
slug: 'favoriten',
status: 'published',
hero: {
headline: 'Meine Favoriten & Tools',
subline: 'Die Dinge, die ich wirklich nutze mit der Rechnung, warum.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Meine Favoriten & Tools',
subline: 'Die Dinge, die ich wirklich nutze mit der Rechnung, warum. Kuratiert, nicht gesponsert.',
alignment: 'center',
},
{
blockType: 'text-block',
width: 'narrow',
content: createRichText(
'Transparenz: Diese Seite enthält Affiliate-Links*. Für dich keine Mehrkosten. Mehr dazu: /transparenz'
),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Fashion', [
'👗 Investment-Pieces, die ich seit Jahren trage. Weniger Teile, bessere Qualität.',
'(Favoriten werden demnächst hinzugefügt)',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Beauty', [
'💄 Meine GRFI-Routine das Minimum, das wirkt.',
'(Favoriten werden demnächst hinzugefügt)',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Tech', [
'💻 Tools für Produktivität & Organisation.',
'(Favoriten werden demnächst hinzugefügt)',
]),
},
],
seo: {
metaTitle: 'Meine Favoriten & Tools | BlogWoman',
metaDescription:
'Die Dinge, die ich wirklich nutze mit der Rechnung, warum. Kuratiert, nicht gesponsert.',
},
},
// SERIEN PAGE
{
title: 'Serien',
slug: 'serien',
status: 'published',
hero: {
headline: 'Die BlogWoman Serien',
subline: 'Wiederkehrende Formate für wiederkehrende Herausforderungen.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Die BlogWoman Serien',
subline:
'Wiederkehrende Formate für wiederkehrende Herausforderungen. Jede Serie löst ein konkretes Problem mit System.',
alignment: 'center',
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Alle Serien', [
'→ GRFI Get Ready For Impact',
'→ Investment-Piece',
'→ Pleasure P&L',
'→ Regeneration',
'→ SPARK',
'→ Inner Circle',
'→ M2M Meeting to Mama',
'→ Decision-Proof',
'→ Backstage',
'',
'(Serien werden über das Admin-Panel verwaltet)',
]),
},
],
seo: {
metaTitle: 'Die BlogWoman Serien | YouTube',
metaDescription:
'Wiederkehrende Formate für wiederkehrende Herausforderungen. GRFI, Investment-Piece, P&L und mehr.',
},
},
// BLOG PAGE
{
title: 'Blog',
slug: 'blog',
status: 'published',
hero: {
headline: 'Der BlogWoman Blog',
subline: 'Systeme für Karriere, Familie & Stil.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Der BlogWoman Blog',
subline: 'Systeme für Karriere, Familie & Stil. Kein Fluff. Kein Clickbait. Nur das, was funktioniert.',
alignment: 'center',
},
{
blockType: 'posts-list-block',
headline: 'Alle Artikel',
limit: 12,
showPagination: true,
},
],
seo: {
metaTitle: 'Der BlogWoman Blog | Karriere, Familie & Stil',
metaDescription:
'Systeme für Karriere, Familie & Stil. Kein Fluff. Kein Clickbait. Nur das, was funktioniert.',
},
},
// KOOPERATIONEN PAGE
{
title: 'Kooperationen',
slug: 'kooperationen',
status: 'published',
hero: {
headline: 'Kooperationen mit BlogWoman',
subline: 'Für Marken, die Frauen mit Anspruch erreichen wollen.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Kooperationen mit BlogWoman',
subline: 'Für Marken, die Frauen mit Anspruch erreichen wollen.',
alignment: 'center',
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Wen Sie erreichen', [
'• Frauen 35-45, DACH',
'• Überdurchschnittliches Haushaltseinkommen',
'• Karriere + Familie',
'• Qualitätsorientiert, ROI-Denken',
'• Entscheiderinnen in Haushalt und Beruf',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('Zusammenarbeit', [
'→ Dedizierte Video-Integration (Longform)',
'→ Shorts-Serie (3-5 Videos)',
'→ Newsletter-Feature (P&L Pick)',
'→ Blog-Artikel (SEO-optimiert)',
'→ Affiliate-Partnerschaft (langfristig)',
]),
},
{
blockType: 'cta-block',
headline: 'Interesse?',
description: 'kooperationen@blogwoman.de Ansprechpartnerin: Dr. Caroline Porwoll',
backgroundColor: 'dark',
buttons: [{ text: 'Kontakt aufnehmen', link: 'mailto:kooperationen@blogwoman.de', style: 'primary' }],
},
],
seo: {
metaTitle: 'Kooperationen | BlogWoman',
metaDescription:
'Für Marken, die Frauen mit Anspruch erreichen wollen. Media Kit und Kontakt.',
},
},
// TRANSPARENZ PAGE
{
title: 'Transparenz',
slug: 'transparenz',
status: 'published',
hero: {
headline: 'Transparenz & Affiliate-Offenlegung',
subline: 'Ehrlichkeit ist ein Kernwert von BlogWoman.',
},
layout: [
{
blockType: 'hero-block',
headline: 'Transparenz & Affiliate-Offenlegung',
subline: 'Ehrlichkeit ist ein Kernwert von BlogWoman.',
alignment: 'center',
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('AFFILIATE-LINKS', [
'Einige Links auf dieser Seite sind Affiliate-Links (*). Wenn du über diese Links kaufst, erhalte ich eine kleine Provision für dich entstehen keine Mehrkosten.',
'Ich empfehle nur Produkte, die ich selbst nutze oder gründlich geprüft habe. Bezahlte Empfehlungen gibt es nicht.',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('SPONSORING', [
'Gesponserte Inhalte werden immer klar gekennzeichnet („Werbung" oder „In Kooperation mit").',
]),
},
{
blockType: 'text-block',
width: 'medium',
content: createRichTextWithHeading('WAS ICH NICHT EMPFEHLE', [
'• Fast Fashion',
'• Detox/Wunder-Produkte',
'• Alkohol, Glücksspiel, Nikotin',
'• Krypto/High-Risk-Investments',
'• Alles, was ich nicht selbst nutzen würde',
'',
'Fragen? hello@blogwoman.de',
]),
},
],
seo: {
metaTitle: 'Transparenz & Affiliate-Offenlegung | BlogWoman',
metaDescription:
'Ehrlichkeit ist ein Kernwert von BlogWoman. Hier findest du volle Transparenz zu Affiliate-Links und Sponsoring.',
},
},
// IMPRESSUM PAGE
{
title: 'Impressum',
slug: 'impressum',
status: 'published',
hero: {},
layout: [
{
blockType: 'text-block',
width: 'narrow',
content: {
root: {
type: 'root',
children: [
{ type: 'heading', tag: 'h1', children: [{ type: 'text', text: 'Impressum' }] },
{
type: 'paragraph',
children: [{ type: 'text', text: 'Angaben gemäß § 5 TMG / § 5 DDG', format: 1 }],
},
{
type: 'paragraph',
children: [
{
type: 'text',
text: 'Complex Care Solutions GmbH\n[Straße und Hausnummer]\n[PLZ] [Ort]\nDeutschland',
},
],
},
{
type: 'paragraph',
children: [{ type: 'text', text: 'Geschäftsführerin: Dr. Caroline Porwoll' }],
},
{ type: 'paragraph', children: [{ type: 'text', text: 'Kontakt', format: 1 }] },
{
type: 'paragraph',
children: [{ type: 'text', text: 'E-Mail: hello@blogwoman.de' }],
},
{ type: 'paragraph', children: [{ type: 'text', text: 'Registereintrag', format: 1 }] },
{
type: 'paragraph',
children: [
{
type: 'text',
text: 'Handelsregister: Amtsgericht [Ort]\nRegisternummer: HRB [Nummer]',
},
],
},
{ type: 'paragraph', children: [{ type: 'text', text: 'Umsatzsteuer-ID', format: 1 }] },
{ type: 'paragraph', children: [{ type: 'text', text: 'USt-IdNr.: DE[Nummer]' }] },
],
direction: 'ltr',
format: '',
indent: 0,
version: 1,
},
},
},
],
seo: {
metaTitle: 'Impressum | BlogWoman',
metaDescription: 'Impressum von BlogWoman betrieben von Complex Care Solutions GmbH.',
},
},
// DATENSCHUTZ PAGE
{
title: 'Datenschutzerklärung',
slug: 'datenschutz',
status: 'published',
hero: {},
layout: [
{
blockType: 'text-block',
width: 'narrow',
content: {
root: {
type: 'root',
children: [
{
type: 'heading',
tag: 'h1',
children: [{ type: 'text', text: 'Datenschutzerklärung' }],
},
{
type: 'heading',
tag: 'h2',
children: [{ type: 'text', text: '1. Datenschutz auf einen Blick' }],
},
{ type: 'paragraph', children: [{ type: 'text', text: 'Allgemeine Hinweise', format: 1 }] },
{
type: 'paragraph',
children: [
{
type: 'text',
text: 'Die folgenden Hinweise geben einen einfachen Überblick darüber, was mit Ihren personenbezogenen Daten passiert, wenn Sie diese Website besuchen.',
},
],
},
{
type: 'heading',
tag: 'h2',
children: [{ type: 'text', text: '2. Verantwortliche Stelle' }],
},
{
type: 'paragraph',
children: [
{
type: 'text',
text: 'Complex Care Solutions GmbH\nGeschäftsführerin: Dr. Caroline Porwoll\nE-Mail: hello@blogwoman.de',
},
],
},
{
type: 'paragraph',
children: [
{
type: 'text',
text: '(Diese Datenschutzerklärung ist ein Platzhalter und muss durch eine vollständige, rechtskonforme Version ersetzt werden.)',
format: 2,
},
],
},
],
direction: 'ltr',
format: '',
indent: 0,
version: 1,
},
},
},
],
seo: {
metaTitle: 'Datenschutzerklärung | BlogWoman',
metaDescription: 'DSGVO-konforme Datenschutzerklärung von BlogWoman.',
},
},
]
const pageIds: Record<string, number> = {}
for (const page of pages) {
const existing = await payload.find({
collection: 'pages',
where: {
and: [{ slug: { equals: page.slug } }, { tenant: { equals: tenantId } }],
},
})
if (existing.docs.length > 0) {
pageIds[page.slug] = existing.docs[0].id as number
await payload.update({
collection: 'pages',
id: existing.docs[0].id,
data: {
...page,
tenant: tenantId,
} as any,
})
console.log(`✓ Updated page: ${page.title}`)
} else {
const created = await payload.create({
collection: 'pages',
data: {
...page,
tenant: tenantId,
} as any,
})
pageIds[page.slug] = created.id as number
console.log(`✓ Created page: ${page.title}`)
}
}
// ============================================
// 2. CREATE BLOG POSTS
// ============================================
console.log('\n--- 2. Creating Blog Posts ---')
const posts = [
{
title: 'Der komplette GRFI-Guide: In 7 Minuten boardroom-ready',
slug: 'grfi-guide',
type: 'blog',
isFeatured: true,
excerpt:
'Wie du in 7-10 Minuten vom Alltag zur Präsenz kommst. Mein komplettes System für Outfit, Grooming und Haltung.',
content: createRichTextWithBullets(
'GRFI Get Ready For Impact',
'In 7-10 Minuten vom Alltag zur Präsenz. So funktioniert mein System:',
[
'Schritt 1: Outfit-Entscheidung in 60 Sekunden (mit Capsule Wardrobe)',
'Schritt 2: 2-Minuten-Grooming-Routine',
'Schritt 3: Haltung und Mindset-Reset',
'Schritt 4: Letzte Kontrolle und Go',
]
),
categories: [categoryIds['stil-wirkung']],
tags: [tagIds['grfi'], tagIds['business-outfit'], tagIds['morgenroutine']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: 'Capsule Wardrobe für berufstätige Mütter: Mein System',
slug: 'capsule-wardrobe-berufstaetige-muetter',
type: 'blog',
isFeatured: false,
excerpt:
'30 Teile, unendliche Kombinationen. So baue ich eine Garderobe, die im Board und auf dem Spielplatz funktioniert.',
content: createRichText([
'Eine Capsule Wardrobe ist kein Verzicht sie ist eine Befreiung.',
'Mit 30 sorgfältig ausgewählten Teilen habe ich unendliche Kombinationsmöglichkeiten. Jedes Teil passt zu mindestens 3 anderen.',
'Das Ergebnis: Weniger Entscheidungsmüdigkeit am Morgen, mehr Zeit für das, was wirklich zählt.',
]),
categories: [categoryIds['stil-wirkung']],
tags: [tagIds['capsule-wardrobe'], tagIds['investment-piece']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: 'Cost-per-Wear erklärt: So rechnest du Investment-Pieces',
slug: 'cost-per-wear-erklaert',
type: 'blog',
isFeatured: false,
excerpt:
'Warum ein 400€-Blazer günstiger sein kann als einer für 60€. Die Mathematik hinter smarten Käufen.',
content: createRichText([
'Cost-per-Wear (CPW) = Kaufpreis ÷ Anzahl der Trageaktionen',
'Ein 400€-Blazer, den du 200x trägst = 2€ CPW',
'Ein 60€-Blazer, den du 10x trägst = 6€ CPW',
'Der teurere Blazer ist am Ende der günstigere. Das ist Investment-Denken.',
]),
categories: [categoryIds['stil-wirkung']],
tags: [tagIds['investment-piece'], tagIds['capsule-wardrobe']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: 'Sunday Reset: 2 Stunden für eine stressfreie Woche',
slug: 'sunday-reset-routine',
type: 'blog',
isFeatured: false,
excerpt: 'Meine wöchentliche Reset-Routine, die mir unter der Woche Stunden spart.',
content: createRichTextWithBullets('Der Sunday Reset', 'So sieht meine Sonntagsroutine aus:', [
'Kalender-Review: Alle Termine der Woche durchgehen',
'Outfit-Planung: 5 Outfits für die Woche zusammenstellen',
'Meal-Prep: Basics für die Woche vorbereiten',
'Mental-Reset: 15 Minuten Journaling',
]),
categories: [categoryIds['systeme-entscheidungen']],
tags: [tagIds['zeitmanagement'], tagIds['morgenroutine']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: '5 Regeln, die mir 5 Stunden pro Woche sparen',
slug: '5-regeln-zeitersparnis',
type: 'blog',
isFeatured: false,
excerpt: 'Keine Hacks. Keine Tricks. Nur Regeln, die funktionieren.',
content: createRichTextWithBullets(
'5 Regeln für mehr Zeit',
'Diese Regeln habe ich über Jahre entwickelt:',
[
'Regel 1: Keine Meetings ohne Agenda',
'Regel 2: E-Mails nur 2x täglich checken',
'Regel 3: Decision-Proof: Wenn X, dann Y',
'Regel 4: Batching: Gleiche Aufgaben bündeln',
'Regel 5: 2-Minuten-Regel: Sofort erledigen, wenn unter 2 Minuten',
]
),
categories: [categoryIds['systeme-entscheidungen']],
tags: [tagIds['zeitmanagement'], tagIds['entscheidungen']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: 'Warum ich BlogWoman gestartet habe',
slug: 'warum-blogwoman',
type: 'blog',
isFeatured: true,
excerpt:
'Die Geschichte hinter BlogWoman. Was mich dazu gebracht hat, diesen Kanal zu starten.',
content: createRichText([
'Es gab diesen Moment vor dem Kleiderschrank. Board-Meeting in 45 Minuten, Kind mit Fieber, keine Ahnung was anziehen.',
'Nicht weil ich nichts hatte. Sondern weil ich kein System hatte.',
'In diesem Moment entstand die Idee für BlogWoman. Ein Ort für Frauen, die wie ich versuchen, Karriere und Familie unter einen Hut zu bekommen.',
'Nicht mit Motivation und Mindset-Sprüchen. Sondern mit Systemen, die funktionieren auch an Tagen, an denen wir nicht funktionieren.',
]),
categories: [categoryIds['karriere-familie']],
tags: [tagIds['working-mom']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
{
title: 'Meine 10 Investment-Pieces (mit CPW nach Jahren)',
slug: 'meine-investment-pieces',
type: 'blog',
isFeatured: false,
excerpt: 'Die Teile, die ich wirklich trage. Mit echten Zahlen nach Jahren der Nutzung.',
content: createRichText([
'Nach 15 Jahren habe ich gelernt: Weniger Teile, bessere Qualität, mehr Kombinationen.',
'Hier sind meine 10 Investment-Pieces mit echten Cost-per-Wear Zahlen nach Jahren der Nutzung.',
'Spoiler: Einige der "teuersten" Stücke haben den niedrigsten CPW.',
]),
categories: [categoryIds['stil-wirkung']],
tags: [tagIds['investment-piece'], tagIds['capsule-wardrobe']],
author: authorId,
status: 'published',
publishedAt: new Date().toISOString(),
},
]
for (const post of posts) {
const existing = await payload.find({
collection: 'posts',
where: {
and: [{ slug: { equals: post.slug } }, { tenant: { equals: tenantId } }],
},
})
if (existing.docs.length > 0) {
console.log(`- Post "${post.title}" already exists`)
} else {
await payload.create({
collection: 'posts',
data: {
...post,
tenant: tenantId,
} as any,
})
console.log(`✓ Created post: ${post.title}`)
}
}
// ============================================
// 3. CREATE NAVIGATION
// ============================================
console.log('\n--- 3. Creating Navigation ---')
const existingNav = await payload.find({
collection: 'navigations',
where: { tenant: { equals: tenantId } },
})
const navigationData = {
title: 'BlogWoman Navigation',
mainMenu: [
{ label: 'Über mich', type: 'custom', url: '/caroline' },
{ label: 'Blog', type: 'custom', url: '/blog' },
{
label: 'Serien',
type: 'submenu',
submenu: [
{ label: 'Alle Serien', linkType: 'custom', url: '/serien' },
{ label: 'GRFI', linkType: 'custom', url: '/serien/grfi' },
{ label: 'Investment-Piece', linkType: 'custom', url: '/serien/investment-piece' },
{ label: 'Pleasure P&L', linkType: 'custom', url: '/serien/pleasure-pl' },
{ label: 'Regeneration', linkType: 'custom', url: '/serien/regeneration' },
{ label: 'SPARK', linkType: 'custom', url: '/serien/spark' },
{ label: 'Inner Circle', linkType: 'custom', url: '/serien/inner-circle' },
],
},
{ label: 'Favoriten', type: 'custom', url: '/favoriten' },
{ label: 'Newsletter', type: 'custom', url: '/newsletter' },
],
footerMenu: [
{ label: 'Über mich', linkType: 'custom', url: '/caroline' },
{ label: 'Blog', linkType: 'custom', url: '/blog' },
{ label: 'Favoriten', linkType: 'custom', url: '/favoriten' },
{ label: 'Newsletter', linkType: 'custom', url: '/newsletter' },
{ label: 'Kooperationen', linkType: 'custom', url: '/kooperationen' },
{ label: 'Impressum', linkType: 'custom', url: '/impressum' },
{ label: 'Datenschutz', linkType: 'custom', url: '/datenschutz' },
{ label: 'Transparenz', linkType: 'custom', url: '/transparenz' },
],
tenant: tenantId,
}
if (existingNav.docs.length > 0) {
await payload.update({
collection: 'navigations',
id: existingNav.docs[0].id,
data: navigationData as any,
})
console.log('✓ Updated navigation')
} else {
await payload.create({
collection: 'navigations',
data: navigationData as any,
})
console.log('✓ Created navigation')
}
// ============================================
// 4. CREATE SOCIAL LINKS
// ============================================
console.log('\n--- 4. Creating Social Links ---')
const socialLinks = [
{ platform: 'youtube', url: 'https://youtube.com/@blogwoman', isActive: true },
{ platform: 'instagram', url: 'https://instagram.com/blogwoman.de', isActive: true },
]
for (const link of socialLinks) {
const existing = await payload.find({
collection: 'social-links',
where: {
and: [{ platform: { equals: link.platform } }, { tenant: { equals: tenantId } }],
},
})
if (existing.docs.length > 0) {
console.log(`- Social link "${link.platform}" already exists`)
} else {
await payload.create({
collection: 'social-links',
data: {
...link,
tenant: tenantId,
} as any,
})
console.log(`✓ Created social link: ${link.platform}`)
}
}
// ============================================
// 5. CREATE COOKIE CONFIGURATION
// ============================================
console.log('\n--- 5. Creating Cookie Configuration ---')
const existingCookieConfig = await payload.find({
collection: 'cookie-configurations',
where: { tenant: { equals: tenantId } },
})
if (existingCookieConfig.docs.length > 0) {
console.log('- Cookie configuration already exists')
} else {
await payload.create({
collection: 'cookie-configurations',
data: {
title: 'BlogWoman Cookie-Einstellungen',
bannerTitle: 'Wir respektieren deine Privatsphäre',
bannerDescription:
'BlogWoman verwendet Cookies, um dein Erlebnis zu verbessern. Du entscheidest, welche Cookies du erlaubst.',
acceptAllText: 'Alle akzeptieren',
acceptNecessaryText: 'Nur notwendige',
settingsText: 'Einstellungen',
tenant: tenantId,
} as any,
})
console.log('✓ Created cookie configuration')
}
// ============================================
// DONE
// ============================================
console.log('\n========================================')
console.log('✅ BlogWoman Tenant Seed (Continuation) completed!')
console.log('========================================')
console.log(`
Summary:
- Tenant ID: ${tenantId}
- Pages created/updated: ${Object.keys(pageIds).length}
- Posts created: ${posts.length}
- Navigation: Configured
- Social Links: YouTube, Instagram
- Cookie Configuration: Created
Note: Favorites were skipped because they require images.
You can add them manually through the admin panel.
`)
process.exit(0)
}
seed().catch((error) => {
console.error('❌ Seed failed:', error)
process.exit(1)
})