diff --git a/docs/KONZEPT-KI-ANLEITUNG.md b/docs/KONZEPT-KI-ANLEITUNG.md new file mode 100644 index 0000000..b7abd98 --- /dev/null +++ b/docs/KONZEPT-KI-ANLEITUNG.md @@ -0,0 +1,805 @@ +# Anleitung: Prompt-Erstellung für Tenant-Setup in Payload CMS + +Diese Anleitung erklärt, wie du als Konzept-KI einen strukturierten Prompt für Claude Code erstellen kannst, um einen neuen Tenant im Payload CMS anzulegen und mit Inhalten zu befüllen. + +--- + +## 1. Projektkontext + +Das Payload CMS ist ein Multi-Tenant-fähiges Headless CMS mit folgenden Eigenschaften: + +- **Framework:** Payload 3.69.0 + Next.js 15.5.9 +- **Datenbank:** PostgreSQL 17 +- **Sprachen:** Deutsch (de, Standard) und Englisch (en) +- **Tenant-Isolation:** Jede Collection ist automatisch tenant-spezifisch + +--- + +## 2. Prompt-Struktur + +Erstelle deinen Prompt nach folgendem Schema: + +```markdown +## Tenant-Informationen + +**Name:** [Firmenname] +**Slug:** [url-freundlicher-name] +**Domain(s):** [domain1.de, domain2.com] + +### E-Mail-Konfiguration (optional) +- From-Adresse: [email] +- From-Name: [Name] +- Reply-To: [email] +- Eigener SMTP: [ja/nein] + - Host: [smtp.example.com] + - Port: [587] + - User: [user] + - Passwort: [pass] + +--- + +## Inhalte + +### Site-Settings +- Logo: [Beschreibung/URL] +- Favicon: [Beschreibung/URL] +- Primärfarbe: [#hex] +- Sekundärfarbe: [#hex] +- Footer-Text: [Text] + +### Navigation +[Liste der Menüpunkte mit Links] + +### Seiten +[Für jede Seite: Titel, Slug, Blocks] + +### Blog-Posts (optional) +[Für jeden Post: Titel, Kategorie, Inhalt] + +### Weitere Inhalte +[Services, Team, Testimonials, etc.] +``` + +--- + +## 3. Verfügbare Collections + +### Kern-Collections +| Collection | Beschreibung | Wichtige Felder | +|------------|--------------|-----------------| +| `tenants` | Tenant-Konfiguration | name, slug, domains, email | +| `site-settings` | Website-Einstellungen | logo, colors, footer, contact | +| `navigations` | Menü-Strukturen | items (array mit label, link, children) | +| `pages` | Website-Seiten | title, slug, blocks (array) | +| `media` | Bilder/Dateien | file, alt, caption | + +### Content-Collections +| Collection | Beschreibung | Wichtige Felder | +|------------|--------------|-----------------| +| `posts` | Blog-Artikel | title, content, categories, tags, authors, type | +| `categories` | Post-Kategorien | name, slug | +| `tags` | Post-Tags | name, slug | +| `authors` | Autoren | name, bio, image | +| `testimonials` | Kundenstimmen | quote, author, company, rating | +| `faqs` | FAQ-Einträge | question, answer | +| `team` | Team-Mitglieder | name, position, bio, image | +| `services` | Dienstleistungen | title, description, icon | +| `service-categories` | Service-Kategorien | name, slug | + +### Erweiterte Collections +| Collection | Beschreibung | Wichtige Felder | +|------------|--------------|-----------------| +| `portfolios` | Portfolio-Einträge | title, images, description, category | +| `products` | Produkte | name, price, description, images | +| `events` | Veranstaltungen | title, date, location, description | +| `jobs` | Stellenangebote | title, department, description, requirements | +| `locations` | Standorte | name, address, coordinates, hours | +| `partners` | Partner/Logos | name, logo, website | +| `downloads` | Downloads | title, file, description | +| `videos` | Video-Bibliothek | title, source (youtube/vimeo/upload), url | +| `timelines` | Zeitstrahlen | title, type, entries | +| `workflows` | Prozesse | title, phases, steps | + +### Formular & Newsletter +| Collection | Beschreibung | +|------------|--------------| +| `forms` | Formular-Builder | +| `newsletter-subscribers` | Newsletter-Abonnenten | + +### Spezial-Collections (tenant-spezifisch) +| Collection | Tenant | Beschreibung | +|------------|--------|--------------| +| `bookings` | porwoll.de | Fotografie-Buchungen | +| `certifications` | c2s | Zertifizierungen | +| `projects` | gunshin | Spieleprojekte | +| `favorites` | BlogWoman | Affiliate-Produkte | +| `series` | BlogWoman | YouTube-Serien | + +--- + +## 4. Verfügbare Blocks (42 Stück) + +### Layout-Blocks +``` +hero-block - Hero-Banner mit Bild +hero-slider-block - Hero-Slider mit mehreren Slides +image-slider-block - Bild-Karussell +text-block - Rich-Text-Inhalt +image-text-block - Bild + Text nebeneinander +card-grid-block - Karten-Raster +quote-block - Zitat/Blockquote +cta-block - Call-to-Action Button +divider-block - Visueller Trenner +``` + +### Media-Blocks +``` +video-block - Video (YouTube/Vimeo/Upload) +video-embed-block - Video-Einbettung mit Privacy-Mode +``` + +### Content-Blocks +``` +posts-list-block - Blog-Post-Liste mit Pagination +testimonials-block - Testimonial-Karussell +newsletter-block - Newsletter-Anmeldung +process-steps-block - Schritt-für-Schritt-Prozess +faq-block - FAQ-Akkordeon +team-block - Team-Mitglieder-Grid +services-block - Service-Auflistung +``` + +### Blog-Blocks +``` +author-bio-block - Autoren-Info +related-posts-block - Verwandte Artikel +share-buttons-block - Social-Share-Buttons +table-of-contents-block - Inhaltsverzeichnis +``` + +### Team-Blocks +``` +team-filter-block - Team mit Filter +org-chart-block - Organigramm +``` + +### Feature-Blocks +``` +locations-block - Standorte mit Karte +logo-grid-block - Partner-/Kunden-Logos +stats-block - Statistiken/Zahlen +jobs-block - Stellenangebote +downloads-block - Download-Bereich +map-block - Karten-Einbettung +events-block - Veranstaltungs-Kalender +pricing-block - Preis-Tabellen +tabs-block - Tab-Inhalte +accordion-block - Akkordeon-Sektionen +comparison-block - Vergleichs-Tabellen +timeline-block - Zeitleisten-Visualisierung +before-after-block - Vorher/Nachher-Vergleich +``` + +### BlogWoman-Blocks +``` +favorites-block - Affiliate-Produkte +series-block - YouTube-Serien-Übersicht +series-detail-block - Einzelne Serie mit Hero +featured-content-block - Kuratierte Inhalte +``` + +--- + +## 5. Block-Konfiguration (Details) + +### hero-block +```json +{ + "blockType": "hero-block", + "heading": "Willkommen", + "subheading": "Untertitel", + "backgroundImage": "", + "ctaText": "Mehr erfahren", + "ctaLink": "/ueber-uns", + "alignment": "center", + "overlay": true, + "overlayOpacity": 0.5 +} +``` + +### hero-slider-block +```json +{ + "blockType": "hero-slider-block", + "slides": [ + { + "heading": "Slide 1", + "subheading": "Text", + "backgroundImage": "", + "ctaText": "Button", + "ctaLink": "/link" + } + ], + "autoplay": true, + "autoplaySpeed": 5000, + "showDots": true, + "showArrows": true +} +``` + +### text-block +```json +{ + "blockType": "text-block", + "content": { + "root": { + "type": "root", + "children": [ + { + "type": "paragraph", + "children": [{"text": "Ihr Text hier..."}] + } + ] + } + } +} +``` + +### image-text-block +```json +{ + "blockType": "image-text-block", + "image": "", + "heading": "Überschrift", + "content": "", + "imagePosition": "left", + "ctaText": "Button", + "ctaLink": "/link" +} +``` + +### card-grid-block +```json +{ + "blockType": "card-grid-block", + "cards": [ + { + "title": "Karte 1", + "description": "Beschreibung", + "image": "", + "link": "/link" + } + ], + "columns": 3 +} +``` + +### faq-block +```json +{ + "blockType": "faq-block", + "heading": "Häufige Fragen", + "faqs": ["", ""] +} +``` + +### testimonials-block +```json +{ + "blockType": "testimonials-block", + "heading": "Kundenstimmen", + "testimonials": ["", ""], + "layout": "carousel" +} +``` + +### team-block +```json +{ + "blockType": "team-block", + "heading": "Unser Team", + "teamMembers": ["", ""], + "showBio": true, + "columns": 4 +} +``` + +### services-block +```json +{ + "blockType": "services-block", + "heading": "Unsere Leistungen", + "services": ["", ""], + "layout": "grid" +} +``` + +### posts-list-block +```json +{ + "blockType": "posts-list-block", + "heading": "Neueste Artikel", + "categories": [""], + "limit": 6, + "showPagination": true +} +``` + +### cta-block +```json +{ + "blockType": "cta-block", + "heading": "Jetzt starten", + "text": "Kontaktieren Sie uns noch heute.", + "buttonText": "Kontakt aufnehmen", + "buttonLink": "/kontakt", + "backgroundColor": "#1a1a1a" +} +``` + +### stats-block +```json +{ + "blockType": "stats-block", + "stats": [ + {"number": "500+", "label": "Kunden"}, + {"number": "10", "label": "Jahre Erfahrung"}, + {"number": "24/7", "label": "Support"} + ] +} +``` + +### pricing-block +```json +{ + "blockType": "pricing-block", + "heading": "Unsere Preise", + "plans": [ + { + "name": "Basic", + "price": "29", + "period": "monatlich", + "features": ["Feature 1", "Feature 2"], + "ctaText": "Auswählen", + "ctaLink": "/checkout/basic", + "highlighted": false + } + ] +} +``` + +--- + +## 6. Beispiel-Prompt (Vollständig) + +```markdown +# Tenant anlegen: Musterfirma GmbH + +## Tenant-Informationen + +**Name:** Musterfirma GmbH +**Slug:** musterfirma +**Domains:** musterfirma.de, www.musterfirma.de + +### E-Mail-Konfiguration +- From-Adresse: info@musterfirma.de +- From-Name: Musterfirma GmbH +- Reply-To: kontakt@musterfirma.de +- Eigener SMTP: nein (globalen SMTP verwenden) + +--- + +## Site-Settings + +- **Logo:** Musterfirma-Logo (blauer Kreis mit weißem "M") +- **Favicon:** Kleines "M" auf blauem Hintergrund +- **Primärfarbe:** #2563eb (Blau) +- **Sekundärfarbe:** #1e40af (Dunkelblau) +- **Akzentfarbe:** #f59e0b (Orange) +- **Footer-Text:** "© 2026 Musterfirma GmbH. Alle Rechte vorbehalten." +- **Kontakt-E-Mail:** info@musterfirma.de +- **Telefon:** +49 123 456789 +- **Adresse:** Musterstraße 1, 12345 Musterstadt + +--- + +## Navigation + +### Hauptmenü +1. Startseite → / +2. Über uns → /ueber-uns +3. Leistungen → /leistungen + - Beratung → /leistungen/beratung + - Entwicklung → /leistungen/entwicklung + - Support → /leistungen/support +4. Referenzen → /referenzen +5. Blog → /blog +6. Kontakt → /kontakt + +### Footer-Navigation +1. Impressum → /impressum +2. Datenschutz → /datenschutz +3. AGB → /agb + +--- + +## Seiten + +### Startseite (/) +**Blocks:** +1. hero-slider-block + - Slide 1: "Willkommen bei Musterfirma" / "Ihr Partner für digitale Lösungen" / CTA: "Jetzt beraten lassen" → /kontakt + - Slide 2: "Innovation trifft Expertise" / "Seit 10 Jahren erfolgreich" / CTA: "Mehr erfahren" → /ueber-uns + +2. stats-block + - 500+ zufriedene Kunden + - 10 Jahre Erfahrung + - 50 Mitarbeiter + - 24/7 Support + +3. services-block + - Überschrift: "Unsere Leistungen" + - Zeige alle Services + +4. testimonials-block + - Überschrift: "Das sagen unsere Kunden" + - 3 Testimonials im Karussell + +5. cta-block + - "Bereit für Ihr nächstes Projekt?" + - Button: "Kostenloses Erstgespräch" → /kontakt + +### Über uns (/ueber-uns) +**Blocks:** +1. hero-block + - "Über Musterfirma" + - "Lernen Sie uns kennen" + +2. image-text-block + - Bild: Team-Foto + - Text: Firmengeschichte und Vision + - Bild links + +3. timeline-block + - Firmengeschichte als Zeitstrahl + - 2016: Gründung + - 2018: Erster Großkunde + - 2020: 25 Mitarbeiter + - 2024: Expansion + +4. team-block + - "Unser Team" + - Alle Team-Mitglieder + +### Leistungen (/leistungen) +**Blocks:** +1. hero-block + - "Unsere Leistungen" + - "Maßgeschneiderte Lösungen für Ihren Erfolg" + +2. services-block + - Alle Services mit Icons + +3. process-steps-block + - "So arbeiten wir" + - Schritt 1: Analyse + - Schritt 2: Konzept + - Schritt 3: Umsetzung + - Schritt 4: Betreuung + +4. cta-block + - "Interesse geweckt?" + - Button: "Jetzt anfragen" + +### Kontakt (/kontakt) +**Blocks:** +1. hero-block + - "Kontakt" + - "Wir freuen uns auf Ihre Nachricht" + +2. image-text-block + - Kontaktinformationen + - Bild: Büro-Foto + +3. contact-form-block + - Kontaktformular + +4. map-block + - Standort auf Karte + +### Blog (/blog) +**Blocks:** +1. hero-block + - "Unser Blog" + - "Insights und Neuigkeiten" + +2. posts-list-block + - Alle Blog-Posts + - 6 pro Seite + - Mit Pagination + +--- + +## Kategorien + +1. **Technologie** (slug: technologie) +2. **Trends** (slug: trends) +3. **Case Studies** (slug: case-studies) +4. **Tipps & Tricks** (slug: tipps-tricks) + +--- + +## Tags + +1. Digitalisierung +2. Innovation +3. KI +4. Cloud +5. Sicherheit + +--- + +## Autoren + +### Max Mustermann +- **Position:** CEO & Gründer +- **Bio:** Max ist Gründer und CEO der Musterfirma GmbH. Mit über 15 Jahren Erfahrung in der IT-Branche... +- **Bild:** Professionelles Porträt + +### Anna Schmidt +- **Position:** Head of Content +- **Bio:** Anna leitet das Content-Team und ist verantwortlich für alle redaktionellen Inhalte... +- **Bild:** Professionelles Porträt + +--- + +## Team-Mitglieder + +### Max Mustermann +- **Position:** CEO & Gründer +- **Bio:** Gründer mit Vision für digitale Transformation +- **E-Mail:** max@musterfirma.de +- **LinkedIn:** linkedin.com/in/maxmustermann + +### Anna Schmidt +- **Position:** Head of Content +- **Bio:** Content-Strategin mit Leidenschaft für Storytelling +- **E-Mail:** anna@musterfirma.de + +### Tim Weber +- **Position:** Lead Developer +- **Bio:** Full-Stack-Entwickler mit Fokus auf skalierbare Lösungen +- **E-Mail:** tim@musterfirma.de + +### Lisa Müller +- **Position:** UX Designer +- **Bio:** Kreiert nutzerzentrierte Designs für digitale Produkte +- **E-Mail:** lisa@musterfirma.de + +--- + +## Services + +### Beratung +- **Icon:** lightbulb +- **Kurzbeschreibung:** Strategische IT-Beratung für Ihr Unternehmen +- **Beschreibung:** Wir analysieren Ihre Geschäftsprozesse und entwickeln maßgeschneiderte Digitalisierungsstrategien... + +### Entwicklung +- **Icon:** code +- **Kurzbeschreibung:** Individuelle Softwareentwicklung +- **Beschreibung:** Unser Entwicklerteam setzt Ihre Ideen in leistungsstarke Anwendungen um... + +### Support +- **Icon:** headset +- **Kurzbeschreibung:** Zuverlässiger 24/7 Support +- **Beschreibung:** Unser Support-Team steht Ihnen rund um die Uhr zur Verfügung... + +--- + +## Testimonials + +### Testimonial 1 +- **Zitat:** "Die Zusammenarbeit mit Musterfirma hat unsere digitale Transformation beschleunigt. Hervorragende Arbeit!" +- **Name:** Dr. Peter Schneider +- **Position:** CTO +- **Unternehmen:** TechCorp AG +- **Rating:** 5 + +### Testimonial 2 +- **Zitat:** "Professionell, zuverlässig und innovativ. Genau der Partner, den wir gesucht haben." +- **Name:** Maria Hofmann +- **Position:** Geschäftsführerin +- **Unternehmen:** Digital Solutions GmbH +- **Rating:** 5 + +### Testimonial 3 +- **Zitat:** "Das Team von Musterfirma versteht es, komplexe Anforderungen in elegante Lösungen zu verwandeln." +- **Name:** Thomas Klein +- **Position:** IT-Leiter +- **Unternehmen:** InnoTech AG +- **Rating:** 5 + +--- + +## FAQs + +### Wie lange dauert ein typisches Projekt? +Die Projektdauer hängt vom Umfang ab. Kleine Projekte dauern 2-4 Wochen, größere Projekte 3-6 Monate. + +### Bieten Sie auch Wartung an? +Ja, wir bieten verschiedene Wartungs- und Support-Pakete an, die auf Ihre Bedürfnisse zugeschnitten sind. + +### Wie läuft die Zusammenarbeit ab? +Nach einem kostenlosen Erstgespräch erstellen wir ein Angebot. Bei Beauftragung starten wir mit einer Analysephase. + +### Arbeiten Sie auch mit kleinen Unternehmen? +Ja, wir betreuen Unternehmen jeder Größe, vom Startup bis zum Konzern. + +--- + +## Blog-Posts + +### Post 1: "Die Zukunft der KI im Mittelstand" +- **Autor:** Max Mustermann +- **Kategorie:** Technologie +- **Tags:** KI, Innovation, Digitalisierung +- **Type:** blog +- **Excerpt:** Wie mittelständische Unternehmen von künstlicher Intelligenz profitieren können... +- **Content:** + - Einleitung zur KI-Revolution + - 3 Anwendungsbeispiele + - Implementierungstipps + - Fazit und Ausblick +- **Status:** published + +### Post 2: "5 Tipps für erfolgreiche Digitalisierung" +- **Autor:** Anna Schmidt +- **Kategorie:** Tipps & Tricks +- **Tags:** Digitalisierung, Tipps +- **Type:** blog +- **Excerpt:** Praktische Ratschläge für Unternehmen auf dem Weg zur Digitalisierung... +- **Content:** + - Einleitung + - Tipp 1-5 mit Erklärungen + - Zusammenfassung +- **Status:** published + +### Post 3: "Case Study: TechCorp digitalisiert Prozesse" +- **Autor:** Max Mustermann +- **Kategorie:** Case Studies +- **Tags:** Case Study, Digitalisierung +- **Type:** blog +- **Excerpt:** Wie wir TechCorp bei der Prozessdigitalisierung unterstützt haben... +- **Content:** + - Ausgangslage + - Herausforderungen + - Lösung + - Ergebnisse +- **Status:** published + +--- + +## Rechtliche Seiten + +### Impressum (/impressum) +- Angaben gemäß § 5 TMG +- Kontaktdaten +- Geschäftsführer +- Handelsregister +- USt-IdNr. + +### Datenschutz (/datenschutz) +- DSGVO-konforme Datenschutzerklärung +- Verantwortlicher +- Datenerfassung +- Cookies +- Rechte der Betroffenen + +### AGB (/agb) +- Allgemeine Geschäftsbedingungen + +--- + +## Formulare + +### Kontaktformular +- Felder: Name, E-Mail, Telefon (optional), Betreff, Nachricht +- Bestätigungs-E-Mail an Absender +- Benachrichtigungs-E-Mail an info@musterfirma.de +``` + +--- + +## 7. Wichtige Hinweise für den Prompt + +### Pflichtangaben +- [ ] Tenant-Name und Slug +- [ ] Mindestens eine Domain +- [ ] Site-Settings (Logo, Farben) +- [ ] Navigation (Header, Footer) +- [ ] Mindestens eine Seite mit Blocks + +### Empfohlene Angaben +- [ ] E-Mail-Konfiguration +- [ ] Team-Mitglieder +- [ ] Services/Leistungen +- [ ] Testimonials +- [ ] FAQ-Einträge +- [ ] Blog-Kategorien und Posts +- [ ] Rechtliche Seiten + +### Formatierung +- Verwende Markdown für Strukturierung +- Nutze klare Überschriften und Listen +- Gib Block-Typen explizit an +- Beschreibe Inhalte so detailliert wie möglich +- Gib Beziehungen zwischen Inhalten an (z.B. Post → Kategorie) + +### Lokalisierung +- Standard-Sprache ist Deutsch (de) +- Gib bei Bedarf englische Übersetzungen an: + ``` + **Titel (DE):** Über uns + **Titel (EN):** About Us + ``` + +--- + +## 8. Ausgabe-Format für Claude + +Dein Prompt sollte Claude bitten, folgende Aufgaben auszuführen: + +1. **Tenant erstellen** via Payload API +2. **Site-Settings anlegen** mit allen Konfigurationen +3. **Navigationen erstellen** (Header, Footer) +4. **Media-Assets hochladen** (falls URLs bereitgestellt) +5. **Kategorien/Tags anlegen** für Blog +6. **Autoren erstellen** für Blog +7. **Team-Mitglieder anlegen** +8. **Services erstellen** +9. **Testimonials anlegen** +10. **FAQs erstellen** +11. **Seiten mit Blocks erstellen** +12. **Blog-Posts anlegen** +13. **Formulare konfigurieren** + +--- + +## 9. Beispiel-Einleitung für deinen Prompt + +```markdown +# Auftrag: Neuen Tenant anlegen und befüllen + +Bitte lege im Payload CMS einen neuen Tenant mit folgenden Daten an. +Erstelle alle notwendigen Inhalte (Collections, Seiten, Posts) gemäß +der Spezifikation unten. + +Nutze die Payload Local API oder REST API für die Datenerstellung. +Stelle sicher, dass alle Beziehungen korrekt verknüpft werden +(z.B. Posts → Categories, Pages → Blocks). + +## Technische Hinweise +- Tenant-ID wird automatisch generiert +- Media-Uploads: Erstelle Platzhalter oder nutze bereitgestellte URLs +- Lexical-Format für Rich-Text-Inhalte verwenden +- Status: 'published' für alle Live-Inhalte + +--- + +[Hier folgen deine Tenant-Daten...] +``` + +--- + +## 10. Checkliste vor Prompt-Übergabe + +- [ ] Alle Pflichtfelder ausgefüllt? +- [ ] Block-Typen korrekt angegeben? +- [ ] Beziehungen zwischen Inhalten definiert? +- [ ] Bilder/Media beschrieben oder URLs bereitgestellt? +- [ ] Texte vollständig formuliert (nicht nur Platzhalter)? +- [ ] Navigation logisch strukturiert? +- [ ] Rechtliche Seiten berücksichtigt? +- [ ] E-Mail-Adressen korrekt formatiert? +- [ ] Farben als Hex-Werte angegeben? diff --git a/prompts/Blogwoman-tenant-prompt.md b/prompts/Blogwoman-tenant-prompt.md new file mode 100644 index 0000000..19353a5 --- /dev/null +++ b/prompts/Blogwoman-tenant-prompt.md @@ -0,0 +1,965 @@ +# Auftrag: Neuen Tenant anlegen und befüllen – BlogWoman + +Bitte lege im Payload CMS (pl.porwoll.tech) einen neuen Tenant mit folgenden Daten an. +Erstelle alle notwendigen Inhalte (Collections, Seiten, Posts) gemäß der Spezifikation unten. + +Nutze die Payload Local API oder REST API für die Datenerstellung. +Stelle sicher, dass alle Beziehungen korrekt verknüpft werden +(z.B. Posts → Categories, Pages → Blocks, Favorites → Categories). + +## Technische Hinweise +- Tenant-ID wird automatisch generiert +- Media-Uploads: Erstelle Platzhalter oder nutze beschriebene Bilder +- Lexical-Format für Rich-Text-Inhalte verwenden +- Status: 'published' für alle Live-Inhalte +- Lokalisierung: Deutsch (de) als Standard + +--- + +## Tenant-Informationen + +**Name:** BlogWoman +**Slug:** blogwoman +**Domain(s):** blogwoman.de, www.blogwoman.de + +### E-Mail-Konfiguration +- From-Adresse: hello@blogwoman.de +- From-Name: BlogWoman +- Reply-To: caroline@blogwoman.de +- Eigener SMTP: nein (globalen SMTP verwenden) + +--- + +## Site-Settings + +### Branding +- **Logo:** BlogWoman Logo (Playfair Display Schriftzug "BLOGWOMAN" in Espresso #2B2520) +- **Logo Subline:** "by Caroline Porwoll" +- **Favicon:** Stylisiertes "B" auf Brass-Hintergrund (#B08D57) + +### Farben (Brand Guidelines) +- **Primärfarbe:** #B08D57 (Brass) +- **Sekundärfarbe:** #2B2520 (Espresso) +- **Akzentfarbe:** #6B1F2B (Bordeaux) +- **Hintergrundfarbe:** #F7F3EC (Ivory) +- **Card-Farbe:** #C6A47E (Sand) +- **Akzent 2:** #D4A5A5 (Rosé - für SPARK-Serie) + +### Kontaktdaten +- **E-Mail:** hello@blogwoman.de +- **Kooperationen:** kooperationen@blogwoman.de +- **Telefon:** (nicht öffentlich) + +### Adresse (Impressum) +- **Firma:** Complex Care Solutions GmbH +- **Straße:** [Firmenadresse einfügen] +- **PLZ/Ort:** [PLZ] [Ort] +- **Land:** Deutschland + +### Social Media +- YouTube: @blogwoman (Haupt-Plattform) +- Instagram: @blogwoman.de + +### Footer +- **Footer-Text:** "© 2025 Complex Care Solutions GmbH" +- **Tagline:** "Für Frauen, die Karriere, Familie & Stil ernst nehmen." + +--- + +## Navigation + +### Header-Navigation (Desktop) +1. Über mich → /caroline +2. Blog → /blog +3. Serien → /serien +4. Favoriten → /favoriten +5. Newsletter (CTA-Button) → /newsletter + +### Header-Navigation (Mobile Menu) +1. Über mich → /caroline +2. Blog → /blog +3. Serien → /serien + - GRFI → /serien/grfi + - Investment-Piece → /serien/investment-piece + - Pleasure P&L → /serien/pleasure-pl + - Regeneration → /serien/regeneration + - SPARK → /serien/spark + - Inner Circle → /serien/inner-circle +4. Favoriten → /favoriten +5. Newsletter → /newsletter +6. Kooperationen → /kooperationen +7. --- +8. YouTube (extern) → https://youtube.com/@blogwoman +9. Instagram (extern) → https://instagram.com/blogwoman.de +10. --- +11. Impressum → /impressum +12. Datenschutz → /datenschutz + +### Footer-Navigation +**Spalte 1: Navigation** +- Über mich → /caroline +- Blog → /blog +- Favoriten → /favoriten +- Newsletter → /newsletter +- Kooperationen → /kooperationen + +**Spalte 2: Serien** +- GRFI → /serien/grfi +- Investment-Piece → /serien/investment-piece +- Pleasure P&L → /serien/pleasure-pl +- SPARK → /serien/spark +- Inner Circle → /serien/inner-circle + +**Spalte 3: Rechtliches** +- Impressum → /impressum +- Datenschutz → /datenschutz +- Transparenz → /transparenz + +--- + +## Kategorien (Posts) + +1. **Stil & Wirkung** (slug: stil-wirkung) + - Beschreibung: GRFI, Investment-Pieces, Capsule Wardrobe, Business-Looks + +2. **Systeme & Entscheidungen** (slug: systeme-entscheidungen) + - Beschreibung: Decision-Proof, Routinen, Delegation, Zeitmanagement + +3. **Regeneration & Energie** (slug: regeneration-energie) + - Beschreibung: Reset-Routinen, Schlaf, Energie-Management, Selfcare + +4. **Karriere x Familie** (slug: karriere-familie) + - Beschreibung: Backstage, Real Talk, Vereinbarkeit, Working Mom + +--- + +## Tags + +1. GRFI +2. Investment-Piece +3. Capsule-Wardrobe +4. Morgenroutine +5. Zeitmanagement +6. Business-Outfit +7. Selfcare +8. Working-Mom +9. Entscheidungen +10. Regeneration + +--- + +## Autoren + +### Dr. Caroline Porwoll (Haupt-Autorin) +- **Name:** Dr. Caroline Porwoll +- **Position:** Gründerin & Host +- **Bio:** Unternehmerin. Mutter. Systemdenkerin. Caroline ist promovierte Wirtschaftswissenschaftlerin, Geschäftsführerin der Complex Care Solutions GmbH und die Stimme hinter BlogWoman. Sie entwickelt Systeme, die Karriere und Familie verbinden – ohne Kompromisse bei beidem. +- **Bild:** Professionelles Portrait (warm, natürliches Licht, Business-Casual) +- **Social Links:** + - YouTube: https://youtube.com/@blogwoman + - Instagram: https://instagram.com/blogwoman.de + +--- + +## Serien (Series Collection) + +### 1. GRFI - Get Ready For Impact +- **Titel (DE):** GRFI – Get Ready For Impact +- **Slug:** grfi +- **Beschreibung:** In 7-10 Minuten vom Alltag zur Präsenz. Outfit. Grooming. Haltung. Mit System. +- **Brand Color:** #B08D57 (Brass) +- **Cover Image:** Caroline im Business-Outfit, Spiegel-Setting +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 2. Investment-Piece +- **Titel (DE):** Investment-Piece +- **Slug:** investment-piece +- **Beschreibung:** 1 Teil, 3 Looks, 1 ehrliche Rechnung. Qualität über Quantität – mit CPW-Analyse. +- **Brand Color:** #2B2520 (Espresso) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 3. Pleasure P&L +- **Titel (DE):** Pleasure P&L +- **Slug:** pleasure-pl +- **Beschreibung:** Gönnen mit ROI. Die Gewinn-und-Verlust-Rechnung für Genuss-Momente. +- **Brand Color:** #6B1F2B (Bordeaux) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 4. Regeneration +- **Titel (DE):** Regeneration +- **Slug:** regeneration +- **Beschreibung:** 20-Minuten-Resets für Körper und Kopf. Energie ist Strategie. +- **Brand Color:** #7BA08A (Sage Green) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 5. SPARK +- **Titel (DE):** SPARK +- **Slug:** spark +- **Beschreibung:** Flamme zurückholen. Für die Momente, wenn alles zu viel wird. +- **Brand Color:** #D4A5A5 (Rosé) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 6. Inner Circle +- **Titel (DE):** Inner Circle +- **Slug:** inner-circle +- **Beschreibung:** Interviews mit Frauen, die Karriere, Familie und Stil leben. +- **Brand Color:** #C6A47E (Sand) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 7. M2M - Meeting to Mama +- **Titel (DE):** M2M – Meeting to Mama +- **Slug:** m2m +- **Beschreibung:** Der Übergang von Business zu Familie. Praktische Tipps für den Switch. +- **Brand Color:** #8B7355 (Warm Brown) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 8. Decision-Proof +- **Titel (DE):** Decision-Proof +- **Slug:** decision-proof +- **Beschreibung:** Regeln schlagen Denken. Systeme für bessere Entscheidungen mit weniger Aufwand. +- **Brand Color:** #4A5568 (Slate) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +### 9. Backstage +- **Titel (DE):** Backstage +- **Slug:** backstage +- **Beschreibung:** Ein Tag, real. Keine Filter, keine Perfektion – so sieht's wirklich aus. +- **Brand Color:** #718096 (Gray) +- **YouTube Playlist ID:** [Playlist-ID einfügen] + +--- + +## Favoriten-Kategorien + +### 1. Fashion +- **Slug:** fashion +- **Icon:** 👗 +- **Beschreibung:** Investment-Pieces, die ich seit Jahren trage. Weniger Teile, bessere Qualität. + +### 2. Beauty +- **Slug:** beauty +- **Icon:** 💄 +- **Beschreibung:** Meine GRFI-Routine – das Minimum, das wirkt. + +### 3. Travel +- **Slug:** travel +- **Icon:** ✈️ +- **Beschreibung:** Business-Reisen & Family-Urlaub. Was mit dabei ist. + +### 4. Tech +- **Slug:** tech +- **Icon:** 💻 +- **Beschreibung:** Tools für Produktivität & Organisation. + +### 5. Home +- **Slug:** home +- **Icon:** 🏠 +- **Beschreibung:** Was mir Zeit spart (und was nicht). + +--- + +## Favoriten (Affiliate-Produkte) – Beispieldaten + +### Fashion-Favoriten + +#### Der perfekte Blazer +- **Titel:** Der Blazer, der alles mitmacht +- **Marke:** [Marke] +- **Kategorie:** fashion +- **Badge:** investment-piece +- **Preis:** ca. 350€ +- **Preis-Range:** premium +- **Affiliate-URL:** [Link] +- **Beschreibung:** "Den Blazer trage ich seit 4 Jahren zu allem – Board, Office, Elternabend. Mein CPW liegt bei unter 5€." +- **Meta:** + - CPW: ~5€ + - Besitze seit: 2021 + - Gesehen in: GRFI Ep. 12 + +#### Die Basis-Bluse +- **Titel:** Weiße Bluse (mein Klassiker) +- **Marke:** [Marke] +- **Kategorie:** fashion +- **Badge:** daily-driver +- **Preis:** ca. 120€ +- **Preis-Range:** mid +- **Beschreibung:** "Besitze 3 Stück davon. Waschen, trocknen, fertig. Nie wieder Bügeln." + +#### Investment Bag +- **Titel:** Die Tasche für alles +- **Marke:** [Marke] +- **Kategorie:** fashion +- **Badge:** grfi-approved +- **Preis:** ca. 890€ +- **Preis-Range:** luxury +- **Beschreibung:** "Laptop, Wickelunterlage, Snacks – passt alles rein. Und sieht trotzdem aus wie eine Business-Tasche." + +### Beauty-Favoriten + +#### GRFI Foundation +- **Titel:** 2-Minuten Foundation +- **Marke:** [Marke] +- **Kategorie:** beauty +- **Badge:** grfi-approved +- **Preis:** ca. 45€ +- **Preis-Range:** mid +- **Beschreibung:** "Sieht nach 8 Stunden Schlaf aus, braucht aber nur 30 Sekunden." + +#### Der rote Lippenstift +- **Titel:** Power-Lippenstift +- **Marke:** [Marke] +- **Kategorie:** beauty +- **Badge:** investment-piece +- **Preis:** ca. 35€ +- **Preis-Range:** mid +- **Beschreibung:** "Mein Geheim-Weapon für wichtige Termine. Verwandelt jedes Outfit." + +### Tech-Favoriten + +#### Kalender-App +- **Titel:** Die Kalender-App, die alles zusammenführt +- **Marke:** [App-Name] +- **Kategorie:** tech +- **Badge:** daily-driver +- **Preis:** 0€ / 5€ Premium +- **Preis-Range:** budget +- **Beschreibung:** "Familie und Business in einem Kalender. Game-Changer." + +#### Noise-Cancelling Kopfhörer +- **Titel:** Meine Fokus-Zone +- **Marke:** [Marke] +- **Kategorie:** tech +- **Badge:** investment-piece +- **Preis:** ca. 380€ +- **Preis-Range:** premium +- **Beschreibung:** "Für Flüge, Home-Office mit Kindern im Haus, oder einfach 30 Minuten Ruhe." + +--- + +## Seiten + +### 1. Startseite (/) + +**Meta:** +- Title: BlogWoman – Karriere, Familie & Stil mit System +- Description: Für Frauen, die Karriere, Familie und Stil ernst nehmen. Systeme statt Motivation. Von Dr. Caroline Porwoll. + +**Blocks:** + +#### Block 1: hero-slider-block +- **Slide 1:** + - Heading: BLOGWOMAN + - Subheading: "Für Frauen, die Karriere, Familie & Stil ernst nehmen." + - Background: Hochwertiges Bild von Caroline, warm beleuchtet + - CTA 1: "Newsletter" → /newsletter + - CTA 2: "Favoriten" → /favoriten + - CTA 3: "Über mich" → /caroline + +#### Block 2: image-text-block (Caroline-Intro) +- **Heading:** Dr. Caroline Porwoll +- **Subheading:** Unternehmerin. Mutter. Systemdenkerin. +- **Content:** "Ich glaube nicht an Work-Life-Balance – ich glaube an Systeme, die beides möglich machen." +- **Image:** Caroline Portrait +- **Image Position:** left +- **CTA:** "Mehr erfahren →" → /caroline + +#### Block 3: featured-content-block (Now Trending) +- **Heading:** Now Trending +- **Type:** mixed +- **Items:** 3 YouTube-Videos + 3 Blog-Artikel (dynamisch oder manuell kuratiert) + +#### Block 4: posts-list-block (Aus dem Blog) +- **Heading:** Aus dem Blog +- **Limit:** 3 +- **Layout:** grid +- **Show Excerpt:** true +- **Show CTA:** "Alle Artikel →" → /blog + +#### Block 5: newsletter-block +- **Heading:** Der BlogWoman Brief +- **Subheading:** Jeden Dienstag in deinem Postfach. +- **Layout:** inline +- **Background:** Sand (#C6A47E) +- **CTA:** "ANMELDEN" +- **Privacy Note:** "Kein Spam. Jederzeit abmelden." + +--- + +### 2. Über mich (/caroline) + +**Meta:** +- Title: Über Caroline | BlogWoman +- Description: Dr. Caroline Porwoll – Unternehmerin, Mutter, Gründerin von BlogWoman. Die Geschichte hinter den Systemen. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Dr. Caroline Porwoll +- **Subheading:** Unternehmerin. Mutter. Die Frau hinter BlogWoman. +- **Background:** Großes, hochwertiges Portrait + +#### Block 2: text-block (Die Kurzversion) +- **Heading:** In 30 Sekunden: +- **Content:** + ``` + • Promovierte Wirtschaftswissenschaftlerin + • Gründerin & Geschäftsführerin der Complex Care Solutions GmbH + • Mutter von [X] Kindern + • 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. + ``` + +#### Block 3: text-block (Der Wendepunkt) +- **Heading:** Es gab diesen Moment +- **Content:** + ``` + 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. + ``` + +#### Block 4: text-block (Meine Prinzipien) +- **Heading:** Wofür ich stehe +- **Content:** + ``` + → 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. + ``` + +#### Block 5: text-block (Was du hier nicht findest) +- **Heading:** Was du hier NICHT findest +- **Content:** + ``` + ✗ Keine „Manifestiere dein bestes Selbst"-Sprüche + ✗ Keine unrealistischen 5-Uhr-Morgenroutinen + ✗ Keine Detox-Wunder oder Trend-Diäten + ✗ Keine Fast-Fashion-Hauls + ✗ Kein Mom-Shaming oder Karriere-Bashing + + Ich glaube daran, dass beides geht. Und ich zeige dir, wie. + ``` + +#### Block 6: stats-block (Credentials) +- **Stats:** + - Dr. rer. pol. + - CEO Complex Care Solutions + - Mutter von [X] Kindern + - 15+ Jahre Erfahrung + +#### Block 7: cta-block (Verbunden bleiben) +- **Heading:** Lass uns verbunden bleiben +- **Buttons:** + - YouTube abonnieren → https://youtube.com/@blogwoman + - Newsletter anmelden → /newsletter + - Instagram folgen → https://instagram.com/blogwoman.de +- **Note:** "Für Kooperationsanfragen: /kooperationen" + +--- + +### 3. Newsletter (/newsletter) + +**Meta:** +- Title: Der BlogWoman Brief | Newsletter +- Description: Performance & Pleasure für Karriere, Familie & Stil. Jeden Dienstag direkt in dein Postfach. Plus: Kostenlose Downloads. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Der BlogWoman Brief +- **Subheading:** Performance & Pleasure für Karriere, Familie & Stil. Jeden Dienstag direkt in dein Postfach. + +#### Block 2: newsletter-block (Main Form) +- **Heading:** Jetzt anmelden +- **Layout:** stacked +- **Fields:** Vorname, E-Mail +- **Lead Magnets (Bullet Points):** + - ☑ GRFI-Checkliste (PDF) + - ☑ 30-Teile Capsule Wardrobe Liste + - ☑ Pleasure P&L Template + +#### Block 3: text-block (Was du bekommst) +- **Heading:** Jeden Dienstag in deinem Postfach: +- **Content:** + ``` + → 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. + ``` + +#### Block 4: text-block (Trust) +- **Content:** + ``` + „Kein Spam. Kein Clickbait. Jederzeit abmelden." + + Schon dabei: [X]+ Frauen, die Karriere, Familie & Stil ernst nehmen. + ``` + +--- + +### 4. Favoriten (/favoriten) + +**Meta:** +- Title: Meine Favoriten & Tools | BlogWoman +- Description: Die Dinge, die ich wirklich nutze – mit der Rechnung, warum. Kuratiert, nicht gesponsert. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Meine Favoriten & Tools +- **Subheading:** Die Dinge, die ich wirklich nutze – mit der Rechnung, warum. Kuratiert, nicht gesponsert. + +#### Block 2: text-block (Transparenz-Hinweis) +- **Content:** "Transparenz: Diese Seite enthält Affiliate-Links*. Für dich keine Mehrkosten. Mehr dazu: /transparenz" + +#### Block 3: favorites-block (Kategorien-Grid) +- **Layout:** category-grid +- **Categories:** fashion, beauty, travel, tech, home +- **Show Description:** true + +--- + +### 5. Favoriten-Kategorie: Fashion (/favoriten/fashion) + +**Meta:** +- Title: Fashion Favoriten | BlogWoman +- Description: Investment-Pieces, die sich rechnen. Weniger Teile, bessere Qualität, mehr Kombinationen. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Fashion Favoriten +- **Subheading:** Investment-Pieces, die sich rechnen. + +#### Block 2: text-block +- **Content:** "Meine Philosophie: Weniger Teile, bessere Qualität, mehr Kombinationen. Hier sind die Stücke, die ich wirklich trage – manche seit 5+ Jahren." + +#### Block 3: favorites-block +- **Category:** fashion +- **Layout:** grid +- **Show CPW:** true +- **Show Badge:** true + +--- + +### 6. Serien-Übersicht (/serien) + +**Meta:** +- Title: Die BlogWoman Serien | YouTube +- Description: Wiederkehrende Formate für wiederkehrende Herausforderungen. GRFI, Investment-Piece, P&L und mehr. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Die BlogWoman Serien +- **Subheading:** Wiederkehrende Formate für wiederkehrende Herausforderungen. Jede Serie löst ein konkretes Problem – mit System. + +#### Block 2: series-block +- **Layout:** grid +- **Show Description:** true +- **Show Video Count:** true + +--- + +### 7. Serien-Detail: GRFI (/serien/grfi) + +**Meta:** +- Title: GRFI – Get Ready For Impact | BlogWoman +- Description: In 7-10 Minuten vom Alltag zur Präsenz. Outfit. Grooming. Haltung. Mit System. + +**Blocks:** + +#### Block 1: series-detail-block +- **Serie:** grfi +- **Show Trailer:** true +- **Show Video List:** true + +#### Block 2: text-block (Für wen) +- **Heading:** Für wen ist GRFI? +- **Content:** + ``` + Für dich, wenn du: + • Morgens wenig Zeit hast, aber präsent wirken musst + • Vor wichtigen Terminen unsicher bist, was du anziehen sollst + • Ein System willst, das auch an Chaos-Tagen funktioniert + ``` + +#### Block 3: card-grid-block (Ressourcen) +- **Heading:** GRFI-Ressourcen +- **Cards:** + - GRFI-Checkliste (PDF) → /newsletter + - Meine GRFI-Favoriten → /favoriten/fashion + - Blog: Der komplette GRFI-Guide → /blog/grfi-guide + +#### Block 4: series-block (Verwandte Serien) +- **Heading:** Verwandte Serien +- **Series:** m2m, investment-piece, regeneration +- **Layout:** horizontal + +--- + +### 8. Blog (/blog) + +**Meta:** +- Title: Der BlogWoman Blog | Karriere, Familie & Stil +- Description: Systeme für Karriere, Familie & Stil. Kein Fluff. Kein Clickbait. Nur das, was funktioniert. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Der BlogWoman Blog +- **Subheading:** Systeme für Karriere, Familie & Stil. Kein Fluff. Kein Clickbait. Nur das, was funktioniert. + +#### Block 2: posts-list-block +- **Layout:** featured-grid (1 groß + weitere) +- **Show Categories:** true +- **Show Reading Time:** true +- **Show Pagination:** true +- **Per Page:** 12 + +--- + +### 9. Kooperationen (/kooperationen) + +**Meta:** +- Title: Kooperationen | BlogWoman +- Description: Für Marken, die Frauen mit Anspruch erreichen wollen. Media Kit und Kontakt. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Kooperationen mit BlogWoman +- **Subheading:** Für Marken, die Frauen mit Anspruch erreichen wollen. + +#### Block 2: text-block (Zielgruppe) +- **Heading:** Wen Sie erreichen +- **Content:** + ``` + • Frauen 35-45, DACH + • Überdurchschnittliches Haushaltseinkommen + • Karriere + Familie + • Qualitätsorientiert, ROI-Denken + • Entscheiderinnen in Haushalt und Beruf + ``` + +#### Block 3: stats-block (Reichweite) +- **Heading:** Reichweite +- **Stats:** + - YouTube: [X] Abonnenten + - Newsletter: [X] Leserinnen + - Instagram: [X] Follower + - Website: [X] monatliche Besucher + +#### Block 4: text-block (Formate) +- **Heading:** Zusammenarbeit +- **Content:** + ``` + → Dedizierte Video-Integration (Longform) + → Shorts-Serie (3-5 Videos) + → Newsletter-Feature (P&L Pick) + → Blog-Artikel (SEO-optimiert) + → Affiliate-Partnerschaft (langfristig) + ``` + +#### Block 5: card-grid-block (Passt / Passt nicht) +- **Cards:** + - **Passt gut:** Premium Fashion & Accessoires, Beauty & Skincare, Travel & Hospitality, Tech & Produktivität, Wellness & Selfcare, Home & Organisation + - **Passt nicht:** Fast Fashion, Detox/Wunder-Supplements, Alkohol/Glücksspiel/Nikotin, Krypto/High-Risk Finance + +#### Block 6: cta-block +- **Heading:** Interesse? +- **Button:** "Media Kit herunterladen (PDF)" → [Download-Link] +- **Text:** "Oder direkt: kooperationen@blogwoman.de\nAnsprechpartnerin: Dr. Caroline Porwoll" + +--- + +### 10. Impressum (/impressum) + +**Meta:** +- Title: Impressum | BlogWoman +- Description: Impressum von BlogWoman – betrieben von Complex Care Solutions GmbH. + +**Blocks:** + +#### Block 1: text-block +- **Heading:** Impressum +- **Content:** + ``` + Angaben gemäß § 5 TMG / § 5 DDG + + Complex Care Solutions GmbH + [Straße und Hausnummer] + [PLZ] [Ort] + Deutschland + + Geschäftsführerin: Dr. Caroline Porwoll + + Kontakt: + E-Mail: hello@blogwoman.de + + Registereintrag: + Handelsregister: Amtsgericht [Ort] + Registernummer: HRB [Nummer] + + Umsatzsteuer-ID: + USt-IdNr.: DE[Nummer] + + Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV: + Dr. Caroline Porwoll + [Adresse] + ``` + +--- + +### 11. Datenschutz (/datenschutz) + +**Meta:** +- Title: Datenschutzerklärung | BlogWoman +- Description: DSGVO-konforme Datenschutzerklärung von BlogWoman. + +**Blocks:** + +#### Block 1: text-block +- **Heading:** Datenschutzerklärung +- **Content:** [DSGVO-konforme Datenschutzerklärung mit Abschnitten zu: Verantwortlicher, Datenerfassung, Newsletter, Analytics, Cookies, Affiliate-Netzwerke, Betroffenenrechte] + +--- + +### 12. Transparenz (/transparenz) + +**Meta:** +- Title: Transparenz & Affiliate-Offenlegung | BlogWoman +- Description: Ehrlichkeit ist ein Kernwert von BlogWoman. Hier findest du volle Transparenz zu Affiliate-Links und Sponsoring. + +**Blocks:** + +#### Block 1: hero-block +- **Heading:** Transparenz & Affiliate-Offenlegung +- **Subheading:** Ehrlichkeit ist ein Kernwert von BlogWoman. + +#### Block 2: text-block +- **Content:** + ``` + 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. + + --- + + SPONSORING + + Gesponserte Inhalte werden immer klar gekennzeichnet + („Werbung" oder „In Kooperation mit"). + + --- + + 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 + ``` + +--- + +## Blog-Posts (Launch-Artikel) + +### Post 1: Der komplette GRFI-Guide +- **Titel:** Der komplette GRFI-Guide: In 7 Minuten boardroom-ready +- **Slug:** grfi-guide +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Stil & Wirkung +- **Tags:** GRFI, Business-Outfit, Morgenroutine +- **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. +- **Status:** published +- **Content:** [Ausführlicher Guide mit Schritt-für-Schritt-Anleitung, Checkliste, Bilder] + +### Post 2: Capsule Wardrobe für berufstätige Mütter +- **Titel:** Capsule Wardrobe für berufstätige Mütter: Mein System +- **Slug:** capsule-wardrobe-berufstaetige-muetter +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Stil & Wirkung +- **Tags:** Capsule-Wardrobe, Investment-Piece +- **Type:** blog +- **isFeatured:** false +- **Excerpt:** 30 Teile, unendliche Kombinationen. So baue ich eine Garderobe, die im Board und auf dem Spielplatz funktioniert. +- **Status:** published + +### Post 3: Cost-per-Wear erklärt +- **Titel:** Cost-per-Wear erklärt: So rechnest du Investment-Pieces +- **Slug:** cost-per-wear-erklaert +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Stil & Wirkung +- **Tags:** Investment-Piece, Capsule-Wardrobe +- **Type:** blog +- **isFeatured:** false +- **Excerpt:** Warum ein 400€-Blazer günstiger sein kann als einer für 60€. Die Mathematik hinter smarten Käufen. +- **Status:** published + +### Post 4: Sunday Reset +- **Titel:** Sunday Reset: 2 Stunden für eine stressfreie Woche +- **Slug:** sunday-reset-routine +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Systeme & Entscheidungen +- **Tags:** Zeitmanagement, Morgenroutine +- **Type:** blog +- **isFeatured:** false +- **Excerpt:** Meine wöchentliche Reset-Routine, die mir unter der Woche Stunden spart. +- **Status:** published + +### Post 5: 5 Regeln für mehr Zeit +- **Titel:** 5 Regeln, die mir 5 Stunden pro Woche sparen +- **Slug:** 5-regeln-zeitersparnis +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Systeme & Entscheidungen +- **Tags:** Zeitmanagement, Entscheidungen +- **Type:** blog +- **isFeatured:** false +- **Excerpt:** Keine Hacks. Keine Tricks. Nur Regeln, die funktionieren. +- **Status:** published + +### Post 6: Warum ich BlogWoman gestartet habe +- **Titel:** Warum ich BlogWoman gestartet habe +- **Slug:** warum-blogwoman +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Karriere x Familie +- **Tags:** Working-Mom +- **Type:** blog +- **isFeatured:** true +- **Excerpt:** Die Geschichte hinter BlogWoman. Was mich dazu gebracht hat, diesen Kanal zu starten. +- **Status:** published + +### Post 7: Meine 10 Investment-Pieces +- **Titel:** Meine 10 Investment-Pieces (mit CPW nach Jahren) +- **Slug:** meine-investment-pieces +- **Autor:** Dr. Caroline Porwoll +- **Kategorie:** Stil & Wirkung +- **Tags:** Investment-Piece, Capsule-Wardrobe +- **Type:** blog +- **isFeatured:** false +- **Excerpt:** Die Teile, die ich wirklich trage. Mit echten Zahlen nach Jahren der Nutzung. +- **Status:** published + +--- + +## Formulare + +### Kontaktformular +- **Name:** Kontaktformular +- **Felder:** + - Name (Text, Pflicht) + - E-Mail (E-Mail, Pflicht) + - Betreff (Dropdown: Allgemeine Anfrage, Kooperation, Presse, Sonstiges) + - Nachricht (Textarea, Pflicht) +- **Bestätigungs-E-Mail:** Ja, an Absender +- **Benachrichtigung:** hello@blogwoman.de + +### Kooperations-Anfrage +- **Name:** Kooperations-Anfrage +- **Felder:** + - Firma/Marke (Text, Pflicht) + - Ansprechpartner (Text, Pflicht) + - E-Mail (E-Mail, Pflicht) + - Website (URL) + - Art der Kooperation (Dropdown: Video-Integration, Shorts, Newsletter, Blog, Affiliate, Sonstiges) + - Budget-Range (Dropdown: <1.000€, 1.000-5.000€, 5.000-10.000€, >10.000€) + - Nachricht (Textarea, Pflicht) +- **Benachrichtigung:** kooperationen@blogwoman.de + +--- + +## SEO-Einstellungen (Global für Tenant) + +- **Title Suffix:** | BlogWoman +- **Default Description:** Für Frauen, die Karriere, Familie und Stil ernst nehmen. Systeme statt Motivation. Von Dr. Caroline Porwoll. +- **Default OG Image:** BlogWoman Social Share Image (1200x630, Logo + Tagline) +- **Robots:** index, follow +- **Google Search Console:** [Code einfügen] + +--- + +## Cookie-Konfiguration + +### Kategorien +1. **Notwendig** (necessary) - Immer aktiv +2. **Funktional** (functional) - Optional +3. **Statistik** (analytics) - Optional (Umami) +4. **Marketing** (marketing) - Optional (Affiliate-Tracking) + +### Cookies dokumentieren +- Umami Analytics (analytics) +- Newsletter-Tool Cookies (functional) +- Affiliate-Netzwerk Cookies (marketing) + +### Banner-Texte (DE) +- **Titel:** Wir respektieren deine Privatsphäre +- **Beschreibung:** BlogWoman verwendet Cookies, um dein Erlebnis zu verbessern. Du entscheidest, welche Cookies du erlaubst. +- **Accept All:** Alle akzeptieren +- **Accept Necessary:** Nur notwendige +- **Settings:** Einstellungen + +--- + +## Zusammenfassung der zu erstellenden Inhalte + +| Content-Typ | Anzahl | +|-------------|--------| +| Tenant | 1 | +| Site-Settings | 1 | +| Navigationen | 2 (Header, Footer) | +| Seiten | 12+ (+ Favoriten-Unterseiten + Serien-Unterseiten) | +| Kategorien | 4 | +| Tags | 10 | +| Autoren | 1 | +| Serien | 9 | +| Favoriten-Kategorien | 5 | +| Favoriten (Produkte) | 7+ (Beispiele) | +| Blog-Posts | 7 | +| Formulare | 2 | +| SEO-Settings | 1 | +| Cookie-Config | 1 | + +--- + +*Prompt-Version: 1.0 | Januar 2025* +*Basierend auf: BlogWoman Website-Struktur v2* diff --git a/prompts/blogwoman-payload-development_1.md b/prompts/blogwoman-payload-development_1.md new file mode 100644 index 0000000..7fca5b2 --- /dev/null +++ b/prompts/blogwoman-payload-development_1.md @@ -0,0 +1,527 @@ +# BlogWoman.de - Payload CMS Erweiterungen + +**Projekt:** cms.c2sgmbh (Payload CMS Multi-Tenant) +**Ziel:** Neue Collections und Blocks für blogwoman.de +**Server:** sv-payload (10.10.181.100) / Production: Hetzner 3 (162.55.85.18) +**Repository:** github.com/complexcaresolutions/cms.c2sgmbh + +--- + +## Kontext + +Das Payload CMS Multi-Tenant-System wird um Features für blogwoman.de erweitert. +BlogWoman ist ein Lifestyle-Blog für berufstätige Frauen mit Fokus auf: +- Affiliate-Produkte (Favoriten) +- YouTube-Serien mit eigenem Branding +- Newsletter-Conversions +- SEO-optimierter Blog-Content + +**Tenant:** blogwoman (muss in Tenants Collection angelegt werden, ID wird dynamisch) + +--- + +## Bestehende Infrastruktur + +### Vorhandene Collections (NICHT ändern) +- Users, Tenants, Media, Pages, Posts, Categories +- Testimonials, FAQs, Team, Services +- NewsletterSubscribers, Videos, VideoCategories +- CookieConfigurations, CookieInventory, ConsentLogs +- AuditLogs, FormSubmissions + +### Vorhandene Blocks (NICHT ändern) +- HeroBlock, HeroSliderBlock, TextBlock, ImageTextBlock +- CardGridBlock, QuoteBlock, CTABlock, VideoBlock +- PostsListBlock, TestimonialsBlock, NewsletterBlock +- FAQBlock, TeamBlock, ServicesBlock, TimelineBlock +- ProcessStepsBlock, DividerBlock, ContactFormBlock + +### Tech Stack +- Payload CMS 3.69.0 +- Next.js 15.5.9 +- React 19.2.3 +- PostgreSQL 17 +- TypeScript +- pnpm + +--- + +## Phase 1: Favorites Collection & Block + +### 1.1 Collection: `favorites` + +**Datei:** `src/collections/Favorites.ts` + +```typescript +// Struktur: +import type { CollectionConfig } from 'payload' + +export const Favorites: CollectionConfig = { + slug: 'favorites', + admin: { + group: 'Content', + useAsTitle: 'title', + defaultColumns: ['title', 'category', 'featured', 'isActive'], + }, + access: { + read: () => true, // Public, aber Tenant-gefiltert + create: ({ req: { user } }) => !!user, + update: ({ req: { user } }) => !!user, + delete: ({ req: { user } }) => !!user, + }, + fields: [ + // ... siehe Feld-Definition unten + ], +} +``` + +**Felder:** + +| Feld | Typ | Config | Pflicht | +|------|-----|--------|---------| +| `title` | text | maxLength: 200 | required | +| `slug` | text | unique, admin.position: 'sidebar' | required | +| `description` | textarea | maxLength: 300 | - | +| `category` | select | options: siehe unten | required | +| `subcategory` | text | maxLength: 100 | - | +| `price` | number | min: 0 | - | +| `priceRange` | select | budget, mid, premium, luxury | - | +| `affiliateUrl` | text | URL-Validierung | required | +| `affiliateNetwork` | select | amazon, awin, ltk, direct, other | - | +| `image` | upload | relationTo: 'media' | required | +| `badge` | select | options: siehe unten | - | +| `featured` | checkbox | defaultValue: false | - | +| `isActive` | checkbox | defaultValue: true | required | +| `order` | number | defaultValue: 0 | - | +| `tenant` | relationship | relationTo: 'tenants' | required | + +**Category Options:** +```typescript +const categoryOptions = [ + { label: 'Fashion', value: 'fashion' }, + { label: 'Beauty', value: 'beauty' }, + { label: 'Travel', value: 'travel' }, + { label: 'Tech', value: 'tech' }, + { label: 'Home', value: 'home' }, +] +``` + +**Badge Options:** +```typescript +const badgeOptions = [ + { label: 'Investment Piece', value: 'investment-piece' }, + { label: 'Daily Driver', value: 'daily-driver' }, + { label: 'GRFI Approved', value: 'grfi-approved' }, + { label: 'Neu', value: 'new' }, + { label: 'Bestseller', value: 'bestseller' }, +] +``` + +**Price Range Options:** +```typescript +const priceRangeOptions = [ + { label: 'Budget (< €50)', value: 'budget' }, + { label: 'Mid (€50-150)', value: 'mid' }, + { label: 'Premium (€150-500)', value: 'premium' }, + { label: 'Luxury (> €500)', value: 'luxury' }, +] +``` + +### 1.2 Block: `favorites-block` + +**Datei:** `src/blocks/FavoritesBlock.ts` + +```typescript +import type { Block } from 'payload' + +export const FavoritesBlock: Block = { + slug: 'favorites-block', + labels: { + singular: 'Favoriten', + plural: 'Favoriten', + }, + fields: [ + // ... siehe unten + ], +} +``` + +**Block-Felder:** + +| Feld | Typ | Default | +|------|-----|---------| +| `title` | text | - | +| `subtitle` | text | - | +| `category` | select | 'all' (+ alle category options) | +| `showFeaturedOnly` | checkbox | false | +| `limit` | number | 8 | +| `layout` | select | 'grid' (grid, list, carousel) | +| `columns` | select | '4' (2, 3, 4) | +| `showPrice` | checkbox | true | +| `showBadge` | checkbox | true | +| `backgroundColor` | select | 'white' | + +--- + +## Phase 2: Series Collection & Blocks + +### 2.1 Collection: `series` + +**Datei:** `src/collections/Series.ts` + +**Felder:** + +| Feld | Typ | Config | Pflicht | +|------|-----|--------|---------| +| `title` | text | localized: true | required | +| `slug` | text | unique | required | +| `tagline` | text | localized: true, maxLength: 150 | - | +| `description` | richText | localized: true | - | +| `logo` | upload | relationTo: 'media' | - | +| `coverImage` | upload | relationTo: 'media' | - | +| `brandColor` | text | placeholder: '#B08D57' | - | +| `accentColor` | text | placeholder: '#FFFFFF' | - | +| `youtubePlaylistId` | text | - | - | +| `youtubePlaylistUrl` | text | URL-Validierung | - | +| `order` | number | defaultValue: 0 | - | +| `isActive` | checkbox | defaultValue: true | required | +| `tenant` | relationship | relationTo: 'tenants' | required | + +**Admin Config:** +```typescript +admin: { + group: 'Content', + useAsTitle: 'title', + defaultColumns: ['title', 'slug', 'brandColor', 'isActive'], +} +``` + +### 2.2 Block: `series-block` + +**Datei:** `src/blocks/SeriesBlock.ts` + +**Felder:** + +| Feld | Typ | Default | +|------|-----|---------| +| `title` | text | - | +| `subtitle` | text | - | +| `layout` | select | 'grid' (grid, list, featured) | +| `showDescription` | checkbox | true | +| `showLogo` | checkbox | true | +| `limit` | number | 6 | +| `columns` | select | '3' (2, 3, 4) | +| `backgroundColor` | select | 'white' | + +### 2.3 Block: `series-detail-block` + +**Datei:** `src/blocks/SeriesDetailBlock.ts` + +Für Serien-Einzelseiten mit Hero und Content. + +**Felder:** + +| Feld | Typ | Default | +|------|-----|---------| +| `series` | relationship | relationTo: 'series' | +| `showHero` | checkbox | true | +| `showDescription` | checkbox | true | +| `showRelatedPosts` | checkbox | true | +| `relatedPostsLimit` | number | 6 | +| `layout` | select | 'full' (full, compact) | + +--- + +## Phase 3: Video Embed Block + +### 3.1 Block: `video-embed-block` + +**Datei:** `src/blocks/VideoEmbedBlock.ts` + +**Felder:** + +| Feld | Typ | Config | +|------|-----|--------| +| `title` | text | optional | +| `videoSource` | select | youtube, vimeo, custom | +| `youtubeUrl` | text | condition: videoSource === 'youtube' | +| `vimeoUrl` | text | condition: videoSource === 'vimeo' | +| `customUrl` | text | condition: videoSource === 'custom' | +| `thumbnail` | upload | relationTo: 'media', optional | +| `caption` | text | localized: true | +| `privacyMode` | checkbox | defaultValue: true | +| `aspectRatio` | select | '16:9' (16:9, 4:3, 1:1, 9:16) | +| `maxWidth` | select | 'large' (full, large, medium, small) | +| `lazyLoad` | checkbox | defaultValue: true | + +**YouTube URL Parser (Utility):** +```typescript +// src/lib/utils/youtube.ts +export function extractYouTubeId(url: string): string | null { + if (!url) return null + const patterns = [ + /youtube\.com\/watch\?v=([^&]+)/, + /youtu\.be\/([^?]+)/, + /youtube\.com\/embed\/([^?]+)/, + /youtube\.com\/shorts\/([^?]+)/, + ] + for (const pattern of patterns) { + const match = url.match(pattern) + if (match) return match[1] + } + return null +} + +export function getYouTubeEmbedUrl(videoId: string, privacyMode = true): string { + const domain = privacyMode ? 'www.youtube-nocookie.com' : 'www.youtube.com' + return `https://${domain}/embed/${videoId}` +} + +export function getYouTubeThumbnail(videoId: string, quality: 'default' | 'hq' | 'maxres' = 'hq'): string { + const qualityMap = { + default: 'default', + hq: 'hqdefault', + maxres: 'maxresdefault', + } + return `https://img.youtube.com/vi/${videoId}/${qualityMap[quality]}.jpg` +} +``` + +--- + +## Phase 4: Stats Block + +### 4.1 Block: `stats-block` + +**Datei:** `src/blocks/StatsBlock.ts` + +**Felder:** + +| Feld | Typ | Config | +|------|-----|--------| +| `title` | text | optional | +| `subtitle` | text | optional | +| `stats` | array | siehe Stats Item | +| `layout` | select | 'row' (row, grid, cards) | +| `columns` | select | '4' (2, 3, 4, 5) | +| `animated` | checkbox | defaultValue: false | +| `backgroundColor` | select | 'white' (white, ivory, sand, dark) | + +**Stats Array Item:** + +| Feld | Typ | Config | +|------|-----|--------| +| `value` | text | required, z.B. "42.000" | +| `prefix` | text | optional, z.B. "€", ">" | +| `suffix` | text | optional, z.B. "+", "%", "K" | +| `label` | text | required, localized | +| `description` | text | optional, localized | +| `icon` | text | optional, Lucide icon name | + +--- + +## Phase 5: Featured Content Block + +### 5.1 Block: `featured-content-block` + +**Datei:** `src/blocks/FeaturedContentBlock.ts` + +**Felder:** + +| Feld | Typ | Config | +|------|-----|--------| +| `title` | text | localized | +| `subtitle` | text | localized | +| `items` | array | siehe Items Array | +| `layout` | select | 'grid' (grid, carousel, list, featured-grid) | +| `columns` | select | '3' (2, 3, 4) | +| `showDates` | checkbox | defaultValue: true | +| `backgroundColor` | select | 'white' | + +**Items Array:** + +| Feld | Typ | Config | +|------|-----|--------| +| `itemType` | select | required: post, video, series, external | +| `post` | relationship | relationTo: 'posts', condition: itemType === 'post' | +| `video` | relationship | relationTo: 'videos', condition: itemType === 'video' | +| `series` | relationship | relationTo: 'series', condition: itemType === 'series' | +| `externalTitle` | text | condition: itemType === 'external' | +| `externalUrl` | text | condition: itemType === 'external' | +| `externalImage` | upload | relationTo: 'media', condition: itemType === 'external' | +| `externalDescription` | textarea | condition: itemType === 'external' | +| `customLabel` | text | optional, z.B. "NEU", "TRENDING" | + +--- + +## Implementierungs-Reihenfolge + +### Schritt 1: Collections erstellen +1. `src/collections/Favorites.ts` +2. `src/collections/Series.ts` + +### Schritt 2: Collections in payload.config.ts registrieren +```typescript +// In payload.config.ts +import { Favorites } from './collections/Favorites' +import { Series } from './collections/Series' + +// In collections Array hinzufügen: +collections: [ + // ... bestehende + Favorites, + Series, +], +``` + +### Schritt 3: Blocks erstellen +1. `src/blocks/FavoritesBlock.ts` +2. `src/blocks/SeriesBlock.ts` +3. `src/blocks/SeriesDetailBlock.ts` +4. `src/blocks/VideoEmbedBlock.ts` +5. `src/blocks/StatsBlock.ts` +6. `src/blocks/FeaturedContentBlock.ts` + +### Schritt 4: Blocks in Pages registrieren +```typescript +// In src/collections/Pages.ts -> layout field -> blocks array +import { FavoritesBlock } from '../blocks/FavoritesBlock' +import { SeriesBlock } from '../blocks/SeriesBlock' +import { SeriesDetailBlock } from '../blocks/SeriesDetailBlock' +import { VideoEmbedBlock } from '../blocks/VideoEmbedBlock' +import { StatsBlock } from '../blocks/StatsBlock' +import { FeaturedContentBlock } from '../blocks/FeaturedContentBlock' + +// blocks: [ +// ... bestehende, +// FavoritesBlock, +// SeriesBlock, +// SeriesDetailBlock, +// VideoEmbedBlock, +// StatsBlock, +// FeaturedContentBlock, +// ] +``` + +### Schritt 5: Utility-Funktionen erstellen +1. `src/lib/utils/youtube.ts` + +### Schritt 6: Migration erstellen +```bash +pnpm payload migrate:create blogwoman_collections +``` + +### Schritt 7: Build & Test +```bash +pnpm lint --fix +pnpm build +``` + +--- + +## Erfolgskriterien + +### Funktional +- [ ] Favorites Collection: CRUD über Admin Panel funktioniert +- [ ] Favorites: Filterung nach Kategorie in API (`/api/favorites?where[category][equals]=fashion`) +- [ ] Favorites: Tenant-Isolation funktioniert +- [ ] Favorites Block: In Pages verwendbar +- [ ] Series Collection: CRUD funktioniert +- [ ] Series: Localization (de/en) funktioniert +- [ ] Series Block: In Pages verwendbar +- [ ] Series Detail Block: Zeigt Serie mit related Posts +- [ ] Video Embed Block: YouTube-URLs werden korrekt geparst +- [ ] Video Embed Block: Privacy Mode (youtube-nocookie) funktioniert +- [ ] Stats Block: Zeigt Statistiken in verschiedenen Layouts +- [ ] Featured Content Block: Verschiedene Content-Typen mischbar + +### Technisch +- [ ] `pnpm lint` ohne Errors +- [ ] `pnpm build` erfolgreich +- [ ] Keine TypeScript-Fehler +- [ ] Migration erstellt und ausführbar +- [ ] API-Endpoints erreichbar: + - GET /api/favorites + - GET /api/series + +### Admin UX +- [ ] Collections erscheinen unter "Content" Gruppe +- [ ] Blocks haben deutsche Labels +- [ ] Conditional Fields funktionieren (z.B. bei itemType) + +--- + +## Escape Hatch + +Falls nach 20+ Iterationen blockiert: + +1. **Dokumentiere in BLOCKERS.md:** + - Was genau nicht funktioniert + - Fehlermeldungen (vollständig) + - Versuchte Lösungsansätze + +2. **Teile das Problem auf:** + - Nur Collections ohne Blocks + - Nur einen Block isoliert + +3. **Prüfe Abhängigkeiten:** + - Ist Payload-Version kompatibel? + - Fehlen Import-Statements? + - Gibt es Naming-Konflikte? + +--- + +## Referenzen + +### Bestehende Collection als Vorlage +- `src/collections/Posts.ts` (für Struktur) +- `src/collections/Testimonials.ts` (für einfache Collection) +- `src/collections/Videos.ts` (falls vorhanden) + +### Bestehende Blocks als Vorlage +- `src/blocks/PostsListBlock.ts` +- `src/blocks/TestimonialsBlock.ts` +- `src/blocks/CardGridBlock.ts` + +### Payload Docs +- Collections: https://payloadcms.com/docs/configuration/collections +- Blocks: https://payloadcms.com/docs/fields/blocks +- Conditional Logic: https://payloadcms.com/docs/fields/overview#conditional-logic + +--- + +## Verzeichnis + +``` +src/ +├── collections/ +│ ├── Favorites.ts # NEU +│ └── Series.ts # NEU +├── blocks/ +│ ├── FavoritesBlock.ts # NEU +│ ├── SeriesBlock.ts # NEU +│ ├── SeriesDetailBlock.ts # NEU +│ ├── VideoEmbedBlock.ts # NEU +│ ├── StatsBlock.ts # NEU +│ └── FeaturedContentBlock.ts # NEU +├── lib/ +│ └── utils/ +│ └── youtube.ts # NEU +└── migrations/ + └── YYYYMMDD_HHMMSS_blogwoman_collections.ts # NEU (auto-generiert) +``` + +--- + +## Fertig? + +Wenn ALLE Erfolgskriterien erfüllt sind: +- Alle Collections erstellt und registriert +- Alle Blocks erstellt und in Pages registriert +- `pnpm lint` ohne Errors +- `pnpm build` erfolgreich +- Migration erstellt + +Dann schreibe als letzte Zeile: + +BLOGWOMAN_PAYLOAD_COMPLETE \ No newline at end of file diff --git a/scripts/seed-blogwoman-continue.ts b/scripts/seed-blogwoman-continue.ts new file mode 100644 index 0000000..50ef489 --- /dev/null +++ b/scripts/seed-blogwoman-continue.ts @@ -0,0 +1,1012 @@ +/** + * 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 = {} + 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 = {} + 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 = {} + + 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) +}) diff --git a/scripts/seed-blogwoman.ts b/scripts/seed-blogwoman.ts new file mode 100644 index 0000000..e888a26 --- /dev/null +++ b/scripts/seed-blogwoman.ts @@ -0,0 +1,1480 @@ +/** + * BlogWoman Tenant Seed Script + * + * Creates the BlogWoman tenant and populates it with all content + * as specified in the Blogwoman-tenant-prompt.md + * + * Run with: npx tsx scripts/seed-blogwoman.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('🚀 Starting BlogWoman Tenant Seed...\n') + + const payload = await getPayload({ config }) + + // ============================================ + // 1. CREATE TENANT + // ============================================ + console.log('--- 1. Creating Tenant ---') + + let tenantId: number + + const existingTenant = await payload.find({ + collection: 'tenants', + where: { slug: { equals: 'blogwoman' } }, + }) + + if (existingTenant.docs.length > 0) { + tenantId = existingTenant.docs[0].id as number + console.log(`✓ Tenant "blogwoman" already exists (ID: ${tenantId})`) + } else { + const tenant = await payload.create({ + collection: 'tenants', + data: { + name: 'BlogWoman', + slug: 'blogwoman', + domains: [{ domain: 'blogwoman.de' }, { domain: 'www.blogwoman.de' }], + email: { + fromAddress: 'hello@blogwoman.de', + fromName: 'BlogWoman', + replyTo: 'caroline@blogwoman.de', + useCustomSmtp: false, + }, + }, + }) + tenantId = tenant.id as number + console.log(`✓ Created tenant "BlogWoman" (ID: ${tenantId})`) + } + + // ============================================ + // 2. CREATE SITE-SETTINGS + // ============================================ + console.log('\n--- 2. Creating Site Settings ---') + + const existingSiteSettings = await payload.find({ + collection: 'site-settings', + where: { tenant: { equals: tenantId } }, + }) + + if (existingSiteSettings.docs.length > 0) { + await payload.update({ + collection: 'site-settings', + id: existingSiteSettings.docs[0].id, + data: { + siteName: 'BlogWoman', + siteTagline: 'Für Frauen, die Karriere, Familie & Stil ernst nehmen.', + contact: { + email: 'hello@blogwoman.de', + }, + footer: { + copyrightText: '© 2025 Complex Care Solutions GmbH', + showSocialLinks: true, + }, + seo: { + defaultMetaTitle: 'BlogWoman – Karriere, Familie & Stil mit System', + defaultMetaDescription: + 'Für Frauen, die Karriere, Familie und Stil ernst nehmen. Systeme statt Motivation. Von Dr. Caroline Porwoll.', + }, + tenant: tenantId, + } as any, + }) + console.log('✓ Updated Site Settings') + } else { + await payload.create({ + collection: 'site-settings', + data: { + siteName: 'BlogWoman', + siteTagline: 'Für Frauen, die Karriere, Familie & Stil ernst nehmen.', + contact: { + email: 'hello@blogwoman.de', + }, + footer: { + copyrightText: '© 2025 Complex Care Solutions GmbH', + showSocialLinks: true, + }, + seo: { + defaultMetaTitle: 'BlogWoman – Karriere, Familie & Stil mit System', + defaultMetaDescription: + 'Für Frauen, die Karriere, Familie und Stil ernst nehmen. Systeme statt Motivation. Von Dr. Caroline Porwoll.', + }, + tenant: tenantId, + } as any, + }) + console.log('✓ Created Site Settings') + } + + // ============================================ + // 3. CREATE CATEGORIES + // ============================================ + console.log('\n--- 3. Creating Categories ---') + + const categories = [ + { + name: 'Stil & Wirkung', + slug: 'stil-wirkung', + description: 'GRFI, Investment-Pieces, Capsule Wardrobe, Business-Looks', + }, + { + name: 'Systeme & Entscheidungen', + slug: 'systeme-entscheidungen', + description: 'Decision-Proof, Routinen, Delegation, Zeitmanagement', + }, + { + name: 'Regeneration & Energie', + slug: 'regeneration-energie', + description: 'Reset-Routinen, Schlaf, Energie-Management, Selfcare', + }, + { + name: 'Karriere x Familie', + slug: 'karriere-familie', + description: 'Backstage, Real Talk, Vereinbarkeit, Working Mom', + }, + ] + + const categoryIds: Record = {} + + for (const cat of categories) { + const existing = await payload.find({ + collection: 'categories', + where: { + and: [{ slug: { equals: cat.slug } }, { tenant: { equals: tenantId } }], + }, + }) + + if (existing.docs.length > 0) { + categoryIds[cat.slug] = existing.docs[0].id as number + console.log(`- Category "${cat.name}" already exists`) + } else { + const created = await payload.create({ + collection: 'categories', + data: { + ...cat, + tenant: tenantId, + } as any, + }) + categoryIds[cat.slug] = created.id as number + console.log(`✓ Created category: ${cat.name}`) + } + } + + // ============================================ + // 4. CREATE TAGS + // ============================================ + console.log('\n--- 4. Creating Tags ---') + + const tags = [ + { name: 'GRFI', slug: 'grfi', color: '#B08D57' }, + { name: 'Investment-Piece', slug: 'investment-piece', color: '#2B2520' }, + { name: 'Capsule-Wardrobe', slug: 'capsule-wardrobe', color: '#C6A47E' }, + { name: 'Morgenroutine', slug: 'morgenroutine', color: '#7BA08A' }, + { name: 'Zeitmanagement', slug: 'zeitmanagement', color: '#4A5568' }, + { name: 'Business-Outfit', slug: 'business-outfit', color: '#B08D57' }, + { name: 'Selfcare', slug: 'selfcare', color: '#D4A5A5' }, + { name: 'Working-Mom', slug: 'working-mom', color: '#6B1F2B' }, + { name: 'Entscheidungen', slug: 'entscheidungen', color: '#4A5568' }, + { name: 'Regeneration', slug: 'regeneration', color: '#7BA08A' }, + ] + + const tagIds: Record = {} + + for (const tag of tags) { + const existing = await payload.find({ + collection: 'tags', + where: { + and: [{ slug: { equals: tag.slug } }, { tenant: { equals: tenantId } }], + }, + }) + + if (existing.docs.length > 0) { + tagIds[tag.slug] = existing.docs[0].id as number + console.log(`- Tag "${tag.name}" already exists`) + } else { + const created = await payload.create({ + collection: 'tags', + data: { + ...tag, + tenant: tenantId, + } as any, + }) + tagIds[tag.slug] = created.id as number + console.log(`✓ Created tag: ${tag.name}`) + } + } + + // ============================================ + // 5. CREATE AUTHOR + // ============================================ + console.log('\n--- 5. Creating Author ---') + + let authorId: number + + const existingAuthor = await payload.find({ + collection: 'authors', + where: { + and: [{ slug: { equals: 'dr-caroline-porwoll' } }, { tenant: { equals: tenantId } }], + }, + }) + + if (existingAuthor.docs.length > 0) { + authorId = existingAuthor.docs[0].id as number + console.log('- Author "Dr. Caroline Porwoll" already exists') + } else { + const author = await payload.create({ + collection: 'authors', + data: { + name: 'Dr. Caroline Porwoll', + slug: 'dr-caroline-porwoll', + title: 'Gründerin & Host', + bioShort: + 'Unternehmerin. Mutter. Systemdenkerin. Caroline ist promovierte Wirtschaftswissenschaftlerin, Geschäftsführerin der Complex Care Solutions GmbH und die Stimme hinter BlogWoman.', + bio: createRichText([ + 'Unternehmerin. Mutter. Systemdenkerin.', + 'Caroline ist promovierte Wirtschaftswissenschaftlerin, Geschäftsführerin der Complex Care Solutions GmbH und die Stimme hinter BlogWoman.', + 'Sie entwickelt Systeme, die Karriere und Familie verbinden – ohne Kompromisse bei beidem.', + ]), + social: { + instagram: 'blogwoman.de', + }, + website: 'https://youtube.com/@blogwoman', + isActive: true, + featured: true, + tenant: tenantId, + } as any, + }) + authorId = author.id as number + console.log('✓ Created author: Dr. Caroline Porwoll') + } + + // ============================================ + // 6. CREATE SERIES + // ============================================ + console.log('\n--- 6. Creating Series ---') + + const seriesData = [ + { + title: 'GRFI – Get Ready For Impact', + slug: 'grfi', + tagline: 'In 7-10 Minuten vom Alltag zur Präsenz.', + description: createRichText( + 'In 7-10 Minuten vom Alltag zur Präsenz. Outfit. Grooming. Haltung. Mit System.' + ), + brandColor: '#B08D57', + order: 1, + }, + { + title: 'Investment-Piece', + slug: 'investment-piece', + tagline: '1 Teil, 3 Looks, 1 ehrliche Rechnung.', + description: createRichText( + '1 Teil, 3 Looks, 1 ehrliche Rechnung. Qualität über Quantität – mit CPW-Analyse.' + ), + brandColor: '#2B2520', + order: 2, + }, + { + title: 'Pleasure P&L', + slug: 'pleasure-pl', + tagline: 'Gönnen mit ROI.', + description: createRichText( + 'Gönnen mit ROI. Die Gewinn-und-Verlust-Rechnung für Genuss-Momente.' + ), + brandColor: '#6B1F2B', + order: 3, + }, + { + title: 'Regeneration', + slug: 'regeneration', + tagline: '20-Minuten-Resets für Körper und Kopf.', + description: createRichText('20-Minuten-Resets für Körper und Kopf. Energie ist Strategie.'), + brandColor: '#7BA08A', + order: 4, + }, + { + title: 'SPARK', + slug: 'spark', + tagline: 'Flamme zurückholen.', + description: createRichText('Flamme zurückholen. Für die Momente, wenn alles zu viel wird.'), + brandColor: '#D4A5A5', + order: 5, + }, + { + title: 'Inner Circle', + slug: 'inner-circle', + tagline: 'Interviews mit inspirierenden Frauen.', + description: createRichText( + 'Interviews mit Frauen, die Karriere, Familie und Stil leben.' + ), + brandColor: '#C6A47E', + order: 6, + }, + { + title: 'M2M – Meeting to Mama', + slug: 'm2m', + tagline: 'Der Übergang von Business zu Familie.', + description: createRichText( + 'Der Übergang von Business zu Familie. Praktische Tipps für den Switch.' + ), + brandColor: '#8B7355', + order: 7, + }, + { + title: 'Decision-Proof', + slug: 'decision-proof', + tagline: 'Regeln schlagen Denken.', + description: createRichText( + 'Regeln schlagen Denken. Systeme für bessere Entscheidungen mit weniger Aufwand.' + ), + brandColor: '#4A5568', + order: 8, + }, + { + title: 'Backstage', + slug: 'backstage', + tagline: 'Ein Tag, real.', + description: createRichText( + "Ein Tag, real. Keine Filter, keine Perfektion – so sieht's wirklich aus." + ), + brandColor: '#718096', + order: 9, + }, + ] + + const seriesIds: Record = {} + + for (const series of seriesData) { + const existing = await payload.find({ + collection: 'series', + where: { + and: [{ slug: { equals: series.slug } }, { tenant: { equals: tenantId } }], + }, + }) + + if (existing.docs.length > 0) { + seriesIds[series.slug] = existing.docs[0].id as number + console.log(`- Series "${series.title}" already exists`) + } else { + const created = await payload.create({ + collection: 'series', + data: { + ...series, + isActive: true, + tenant: tenantId, + } as any, + }) + seriesIds[series.slug] = created.id as number + console.log(`✓ Created series: ${series.title}`) + } + } + + // ============================================ + // 7. CREATE FAVORITES + // ============================================ + console.log('\n--- 7. Creating Favorites ---') + + const favoritesData = [ + { + title: 'Der Blazer, der alles mitmacht', + slug: 'blazer-klassiker', + description: + 'Den Blazer trage ich seit 4 Jahren zu allem – Board, Office, Elternabend. Mein CPW liegt bei unter 5€.', + category: 'fashion', + badge: 'investment-piece', + price: 350, + priceRange: 'premium', + affiliateUrl: 'https://example.com/blazer', + order: 1, + }, + { + title: 'Weiße Bluse (mein Klassiker)', + slug: 'weisse-bluse', + description: 'Besitze 3 Stück davon. Waschen, trocknen, fertig. Nie wieder Bügeln.', + category: 'fashion', + badge: 'daily-driver', + price: 120, + priceRange: 'mid', + affiliateUrl: 'https://example.com/bluse', + order: 2, + }, + { + title: 'Die Tasche für alles', + slug: 'business-tasche', + description: + 'Laptop, Wickelunterlage, Snacks – passt alles rein. Und sieht trotzdem aus wie eine Business-Tasche.', + category: 'fashion', + badge: 'grfi-approved', + price: 890, + priceRange: 'luxury', + affiliateUrl: 'https://example.com/tasche', + order: 3, + }, + { + title: '2-Minuten Foundation', + slug: 'grfi-foundation', + description: 'Sieht nach 8 Stunden Schlaf aus, braucht aber nur 30 Sekunden.', + category: 'beauty', + badge: 'grfi-approved', + price: 45, + priceRange: 'mid', + affiliateUrl: 'https://example.com/foundation', + order: 1, + }, + { + title: 'Power-Lippenstift', + slug: 'power-lippenstift', + description: 'Mein Geheim-Weapon für wichtige Termine. Verwandelt jedes Outfit.', + category: 'beauty', + badge: 'investment-piece', + price: 35, + priceRange: 'mid', + affiliateUrl: 'https://example.com/lippenstift', + order: 2, + }, + { + title: 'Die Kalender-App, die alles zusammenführt', + slug: 'kalender-app', + description: 'Familie und Business in einem Kalender. Game-Changer.', + category: 'tech', + badge: 'daily-driver', + price: 0, + priceRange: 'budget', + affiliateUrl: 'https://example.com/kalender', + order: 1, + }, + { + title: 'Meine Fokus-Zone', + slug: 'noise-cancelling-kopfhoerer', + description: + 'Für Flüge, Home-Office mit Kindern im Haus, oder einfach 30 Minuten Ruhe.', + category: 'tech', + badge: 'investment-piece', + price: 380, + priceRange: 'premium', + affiliateUrl: 'https://example.com/kopfhoerer', + order: 2, + }, + ] + + for (const fav of favoritesData) { + const existing = await payload.find({ + collection: 'favorites', + where: { + and: [{ slug: { equals: fav.slug } }, { tenant: { equals: tenantId } }], + }, + }) + + if (existing.docs.length > 0) { + console.log(`- Favorite "${fav.title}" already exists`) + } else { + await payload.create({ + collection: 'favorites', + data: { + ...fav, + isActive: true, + featured: fav.badge === 'investment-piece', + tenant: tenantId, + } as any, + }) + console.log(`✓ Created favorite: ${fav.title}`) + } + } + + // ============================================ + // 8. CREATE PAGES + // ============================================ + console.log('\n--- 8. 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: 'image-text-block', + headline: 'Dr. Caroline Porwoll', + imagePosition: 'left', + content: createRichText([ + 'Unternehmerin. Mutter. Systemdenkerin.', + 'Ich glaube nicht an Work-Life-Balance – ich glaube an Systeme, die beides möglich machen.', + ]), + cta: { + text: 'Mehr erfahren →', + link: '/caroline', + }, + }, + { + 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: 'text-block', + width: 'medium', + content: createRichTextWithHeading('Was du hier NICHT findest', [ + '✗ Keine „Manifestiere dein bestes Selbst"-Sprüche', + '✗ Keine unrealistischen 5-Uhr-Morgenroutinen', + '✗ Keine Detox-Wunder oder Trend-Diäten', + '✗ Keine Fast-Fashion-Hauls', + '✗ Kein Mom-Shaming oder Karriere-Bashing', + '', + 'Ich glaube daran, dass beides geht. Und ich zeige dir, wie.', + ]), + }, + { + 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.', + ]), + }, + { + blockType: 'text-block', + width: 'narrow', + content: createRichText([ + '„Kein Spam. Kein Clickbait. Jederzeit abmelden."', + 'Schon dabei: Frauen, die Karriere, Familie & Stil ernst nehmen.', + ]), + }, + ], + 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: 'favorites-block', + headline: 'Fashion', + category: 'fashion', + layout: 'grid', + }, + { + blockType: 'favorites-block', + headline: 'Beauty', + category: 'beauty', + layout: 'grid', + }, + { + blockType: 'favorites-block', + headline: 'Tech', + category: 'tech', + layout: 'grid', + }, + ], + 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: 'series-block', + headline: 'Alle Serien', + layout: 'grid', + showDescription: true, + }, + ], + 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: 'text-block', + width: 'medium', + content: createRichTextWithHeading('Passt gut', [ + 'Premium Fashion & Accessoires, Beauty & Skincare, Travel & Hospitality, Tech & Produktivität, Wellness & Selfcare, Home & Organisation', + ]), + }, + { + blockType: 'text-block', + width: 'medium', + content: createRichTextWithHeading('Passt nicht', [ + 'Fast Fashion, Detox/Wunder-Supplements, Alkohol/Glücksspiel/Nikotin, Krypto/High-Risk Finance', + ]), + }, + { + 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]' }] }, + { + type: 'paragraph', + children: [ + { + type: 'text', + text: 'Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV:', + format: 1, + }, + ], + }, + { + type: 'paragraph', + children: [{ type: 'text', text: 'Dr. Caroline Porwoll\n[Adresse]' }], + }, + ], + 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: 'heading', + tag: 'h2', + children: [{ type: 'text', text: '3. Newsletter' }], + }, + { + type: 'paragraph', + children: [ + { + type: 'text', + text: 'Wenn Sie den auf der Website angebotenen Newsletter beziehen möchten, benötigen wir von Ihnen eine E-Mail-Adresse sowie Informationen, welche uns die Überprüfung gestatten, dass Sie der Inhaber der angegebenen E-Mail-Adresse sind.', + }, + ], + }, + { + 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 = {} + + 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}`) + } + } + + // ============================================ + // 9. CREATE BLOG POSTS + // ============================================ + console.log('\n--- 9. 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}`) + } + } + + // ============================================ + // 10. CREATE NAVIGATION + // ============================================ + console.log('\n--- 10. 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') + } + + // ============================================ + // 11. CREATE SOCIAL LINKS + // ============================================ + console.log('\n--- 11. 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}`) + } + } + + // ============================================ + // 12. CREATE COOKIE CONFIGURATION + // ============================================ + console.log('\n--- 12. 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 completed!') + console.log('========================================') + console.log(` +Summary: +- Tenant ID: ${tenantId} +- Categories: ${Object.keys(categoryIds).length} +- Tags: ${Object.keys(tagIds).length} +- Series: ${Object.keys(seriesIds).length} +- Pages: ${Object.keys(pageIds).length} +- Posts: ${posts.length} +- Author: Dr. Caroline Porwoll +- Navigation: Configured +- Social Links: YouTube, Instagram +`) + + process.exit(0) +} + +seed().catch((error) => { + console.error('❌ Seed failed:', error) + process.exit(1) +}) diff --git a/src/migrations/20260109_030000_add_blogwoman_block_tables.ts b/src/migrations/20260109_030000_add_blogwoman_block_tables.ts new file mode 100644 index 0000000..d214b51 --- /dev/null +++ b/src/migrations/20260109_030000_add_blogwoman_block_tables.ts @@ -0,0 +1,511 @@ +import { MigrateUpArgs, MigrateDownArgs, sql } from '@payloadcms/db-postgres' + +/** + * Migration: Add BlogWoman Block Tables + * + * Creates the missing pages_blocks_* tables for BlogWoman blocks: + * - favorites-block + * - series-block + * - series-detail-block + * - featured-content-block + */ +export async function up({ db, payload, req }: MigrateUpArgs): Promise { + await db.execute(sql` +-- ===================================================== +-- ENUM TYPES for BlogWoman Blocks +-- ===================================================== + +-- FavoritesBlock ENUMs +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_favorites_block_category" AS ENUM('all', 'fashion', 'beauty', 'travel', 'tech', 'home'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_favorites_block_layout" AS ENUM('grid', 'list', 'carousel'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_favorites_block_columns" AS ENUM('2', '3', '4'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_favorites_block_background_color" AS ENUM('white', 'ivory', 'sand', 'light', 'dark'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- SeriesBlock ENUMs +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_block_layout" AS ENUM('grid', 'list', 'featured'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_block_columns" AS ENUM('2', '3', '4'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_block_background_color" AS ENUM('white', 'ivory', 'sand', 'light', 'dark'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- SeriesDetailBlock ENUMs +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_detail_block_layout" AS ENUM('full', 'compact'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_detail_block_hero_height" AS ENUM('small', 'medium', 'large', 'fullscreen'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_series_detail_block_hero_text_align" AS ENUM('left', 'center', 'right'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- FeaturedContentBlock ENUMs +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_layout" AS ENUM('grid', 'carousel', 'list', 'featured-grid'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_columns" AS ENUM('2', '3', '4'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_background_color" AS ENUM('white', 'ivory', 'sand', 'light', 'dark'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_card_bg" AS ENUM('white', 'transparent', 'light'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_card_img_ratio" AS ENUM('16:9', '4:3', '1:1', '3:2'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_card_hover" AS ENUM('none', 'lift', 'zoom', 'shadow'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + CREATE TYPE "public"."enum_pages_blocks_featured_content_block_items_item_type" AS ENUM('post', 'video', 'series', 'external'); +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +-- ===================================================== +-- FAVORITES BLOCK TABLES +-- ===================================================== + +CREATE TABLE IF NOT EXISTS "pages_blocks_favorites_block" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "category" "enum_pages_blocks_favorites_block_category" DEFAULT 'all', + "show_featured_only" boolean DEFAULT false, + "limit" numeric DEFAULT 8, + "layout" "enum_pages_blocks_favorites_block_layout" DEFAULT 'grid', + "columns" "enum_pages_blocks_favorites_block_columns" DEFAULT '4', + "show_price" boolean DEFAULT true, + "show_badge" boolean DEFAULT true, + "show_description" boolean DEFAULT false, + "show_category" boolean DEFAULT false, + "background_color" "enum_pages_blocks_favorites_block_background_color" DEFAULT 'white', + "cta_show_cta" boolean DEFAULT false, + "cta_cta_url" varchar, + "block_name" varchar +); + +CREATE TABLE IF NOT EXISTS "pages_blocks_favorites_block_locales" ( + "title" varchar, + "subtitle" varchar, + "cta_cta_text" varchar DEFAULT 'Alle Favoriten ansehen', + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL, + CONSTRAINT "pages_blocks_favorites_block_locales_locale_parent_id_unique" UNIQUE("_locale","_parent_id") +); + +-- ===================================================== +-- SERIES BLOCK TABLES +-- ===================================================== + +CREATE TABLE IF NOT EXISTS "pages_blocks_series_block" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "layout" "enum_pages_blocks_series_block_layout" DEFAULT 'grid', + "columns" "enum_pages_blocks_series_block_columns" DEFAULT '3', + "show_description" boolean DEFAULT true, + "show_logo" boolean DEFAULT true, + "show_tagline" boolean DEFAULT true, + "use_brand_colors" boolean DEFAULT true, + "limit" numeric DEFAULT 6, + "background_color" "enum_pages_blocks_series_block_background_color" DEFAULT 'white', + "cta_show_cta" boolean DEFAULT false, + "cta_cta_url" varchar, + "block_name" varchar +); + +CREATE TABLE IF NOT EXISTS "pages_blocks_series_block_locales" ( + "title" varchar, + "subtitle" varchar, + "cta_cta_text" varchar DEFAULT 'Alle Serien ansehen', + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL, + CONSTRAINT "pages_blocks_series_block_locales_locale_parent_id_unique" UNIQUE("_locale","_parent_id") +); + +-- ===================================================== +-- SERIES DETAIL BLOCK TABLES +-- ===================================================== + +CREATE TABLE IF NOT EXISTS "pages_blocks_series_detail_block" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "series_id" integer, + "show_hero" boolean DEFAULT true, + "show_description" boolean DEFAULT true, + "show_brand_colors" boolean DEFAULT true, + "show_related_posts" boolean DEFAULT true, + "related_posts_limit" numeric DEFAULT 6, + "show_youtube_playlist" boolean DEFAULT true, + "layout" "enum_pages_blocks_series_detail_block_layout" DEFAULT 'full', + "hero_height" "enum_pages_blocks_series_detail_block_hero_height" DEFAULT 'medium', + "hero_overlay" boolean DEFAULT true, + "hero_text_align" "enum_pages_blocks_series_detail_block_hero_text_align" DEFAULT 'center', + "block_name" varchar +); + +CREATE TABLE IF NOT EXISTS "pages_blocks_series_detail_block_locales" ( + "related_posts_title" varchar DEFAULT 'Beiträge aus dieser Serie', + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL, + CONSTRAINT "pages_blocks_series_detail_block_locales_locale_parent_id_unique" UNIQUE("_locale","_parent_id") +); + +-- Relationship table for series field +CREATE TABLE IF NOT EXISTS "pages_blocks_series_detail_block_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" varchar NOT NULL, + "path" varchar NOT NULL, + "series_id" integer +); + +-- ===================================================== +-- FEATURED CONTENT BLOCK TABLES +-- ===================================================== + +CREATE TABLE IF NOT EXISTS "pages_blocks_featured_content_block" ( + "_order" integer NOT NULL, + "_parent_id" integer NOT NULL, + "_path" text NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "layout" "enum_pages_blocks_featured_content_block_layout" DEFAULT 'grid', + "columns" "enum_pages_blocks_featured_content_block_columns" DEFAULT '3', + "show_dates" boolean DEFAULT true, + "show_type" boolean DEFAULT true, + "show_description" boolean DEFAULT true, + "show_custom_labels" boolean DEFAULT true, + "background_color" "enum_pages_blocks_featured_content_block_background_color" DEFAULT 'white', + "card_bg" "enum_pages_blocks_featured_content_block_card_bg" DEFAULT 'white', + "card_shadow" boolean DEFAULT true, + "card_border" boolean DEFAULT false, + "card_img_ratio" "enum_pages_blocks_featured_content_block_card_img_ratio" DEFAULT '16:9', + "card_hover" "enum_pages_blocks_featured_content_block_card_hover" DEFAULT 'lift', + "cta_show_cta" boolean DEFAULT false, + "cta_cta_url" varchar, + "block_name" varchar +); + +CREATE TABLE IF NOT EXISTS "pages_blocks_featured_content_block_locales" ( + "title" varchar, + "subtitle" varchar, + "cta_cta_text" varchar DEFAULT 'Alle ansehen', + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL, + CONSTRAINT "pages_blocks_featured_content_block_locales_locale_parent_id_unique" UNIQUE("_locale","_parent_id") +); + +-- Items array table for featured content +CREATE TABLE IF NOT EXISTS "pages_blocks_featured_content_block_items" ( + "_order" integer NOT NULL, + "_parent_id" varchar NOT NULL, + "id" varchar PRIMARY KEY NOT NULL, + "item_type" "enum_pages_blocks_featured_content_block_items_item_type", + "post_id" integer, + "video_id" integer, + "series_id" integer, + "external_url" varchar, + "external_image_id" integer, + "featured" boolean DEFAULT false +); + +CREATE TABLE IF NOT EXISTS "pages_blocks_featured_content_block_items_locales" ( + "external_title" varchar, + "external_description" varchar, + "custom_label" varchar, + "id" serial PRIMARY KEY NOT NULL, + "_locale" "_locales" NOT NULL, + "_parent_id" varchar NOT NULL, + CONSTRAINT "pages_blocks_featured_content_block_items_locales_locale_parent_id_unique" UNIQUE("_locale","_parent_id") +); + +-- Relationship table for items (post, video, series references) +CREATE TABLE IF NOT EXISTS "pages_blocks_featured_content_block_items_rels" ( + "id" serial PRIMARY KEY NOT NULL, + "order" integer, + "parent_id" varchar NOT NULL, + "path" varchar NOT NULL, + "posts_id" integer, + "videos_id" integer, + "series_id" integer +); + +-- ===================================================== +-- INDEXES +-- ===================================================== + +-- Favorites Block indexes +CREATE INDEX IF NOT EXISTS "pages_blocks_favorites_block_order_idx" ON "pages_blocks_favorites_block" ("_order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_favorites_block_parent_id_idx" ON "pages_blocks_favorites_block" ("_parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_favorites_block_path_idx" ON "pages_blocks_favorites_block" ("_path"); + +-- Series Block indexes +CREATE INDEX IF NOT EXISTS "pages_blocks_series_block_order_idx" ON "pages_blocks_series_block" ("_order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_block_parent_id_idx" ON "pages_blocks_series_block" ("_parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_block_path_idx" ON "pages_blocks_series_block" ("_path"); + +-- Series Detail Block indexes +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_order_idx" ON "pages_blocks_series_detail_block" ("_order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_parent_id_idx" ON "pages_blocks_series_detail_block" ("_parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_path_idx" ON "pages_blocks_series_detail_block" ("_path"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_series_idx" ON "pages_blocks_series_detail_block" ("series_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_rels_order_idx" ON "pages_blocks_series_detail_block_rels" ("order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_rels_parent_fk" ON "pages_blocks_series_detail_block_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_rels_path_idx" ON "pages_blocks_series_detail_block_rels" ("path"); +CREATE INDEX IF NOT EXISTS "pages_blocks_series_detail_block_rels_series_idx" ON "pages_blocks_series_detail_block_rels" ("series_id"); + +-- Featured Content Block indexes +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_order_idx" ON "pages_blocks_featured_content_block" ("_order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_parent_id_idx" ON "pages_blocks_featured_content_block" ("_parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_path_idx" ON "pages_blocks_featured_content_block" ("_path"); + +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_order_idx" ON "pages_blocks_featured_content_block_items" ("_order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_parent_fk" ON "pages_blocks_featured_content_block_items" ("_parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_post_idx" ON "pages_blocks_featured_content_block_items" ("post_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_video_idx" ON "pages_blocks_featured_content_block_items" ("video_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_series_idx" ON "pages_blocks_featured_content_block_items" ("series_id"); + +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_rels_order_idx" ON "pages_blocks_featured_content_block_items_rels" ("order"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_rels_parent_fk" ON "pages_blocks_featured_content_block_items_rels" ("parent_id"); +CREATE INDEX IF NOT EXISTS "pages_blocks_featured_content_block_items_rels_path_idx" ON "pages_blocks_featured_content_block_items_rels" ("path"); + +-- ===================================================== +-- FOREIGN KEY CONSTRAINTS +-- ===================================================== + +DO $$ BEGIN + ALTER TABLE "pages_blocks_favorites_block" ADD CONSTRAINT "pages_blocks_favorites_block_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_favorites_block_locales" ADD CONSTRAINT "pages_blocks_favorites_block_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_favorites_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_block" ADD CONSTRAINT "pages_blocks_series_block_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_block_locales" ADD CONSTRAINT "pages_blocks_series_block_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_series_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_detail_block" ADD CONSTRAINT "pages_blocks_series_detail_block_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_detail_block_locales" ADD CONSTRAINT "pages_blocks_series_detail_block_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_series_detail_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_detail_block_rels" ADD CONSTRAINT "pages_blocks_series_detail_block_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."pages_blocks_series_detail_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_detail_block_rels" ADD CONSTRAINT "pages_blocks_series_detail_block_rels_series_fk" FOREIGN KEY ("series_id") REFERENCES "public"."series"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_series_detail_block" ADD CONSTRAINT "pages_blocks_series_detail_block_series_fk" FOREIGN KEY ("series_id") REFERENCES "public"."series"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block" ADD CONSTRAINT "pages_blocks_featured_content_block_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_locales" ADD CONSTRAINT "pages_blocks_featured_content_block_locales_parent_id_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_featured_content_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items" ADD CONSTRAINT "pages_blocks_featured_content_block_items_parent_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_featured_content_block"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items" ADD CONSTRAINT "pages_blocks_featured_content_block_items_external_image_fk" FOREIGN KEY ("external_image_id") REFERENCES "public"."media"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items" ADD CONSTRAINT "pages_blocks_featured_content_block_items_post_fk" FOREIGN KEY ("post_id") REFERENCES "public"."posts"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items" ADD CONSTRAINT "pages_blocks_featured_content_block_items_video_fk" FOREIGN KEY ("video_id") REFERENCES "public"."videos"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items" ADD CONSTRAINT "pages_blocks_featured_content_block_items_series_fk" FOREIGN KEY ("series_id") REFERENCES "public"."series"("id") ON DELETE set null ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items_locales" ADD CONSTRAINT "pages_blocks_featured_content_block_items_locales_parent_fk" FOREIGN KEY ("_parent_id") REFERENCES "public"."pages_blocks_featured_content_block_items"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items_rels" ADD CONSTRAINT "pages_blocks_featured_content_block_items_rels_parent_fk" FOREIGN KEY ("parent_id") REFERENCES "public"."pages_blocks_featured_content_block_items"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items_rels" ADD CONSTRAINT "pages_blocks_featured_content_block_items_rels_posts_fk" FOREIGN KEY ("posts_id") REFERENCES "public"."posts"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items_rels" ADD CONSTRAINT "pages_blocks_featured_content_block_items_rels_videos_fk" FOREIGN KEY ("videos_id") REFERENCES "public"."videos"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + +DO $$ BEGIN + ALTER TABLE "pages_blocks_featured_content_block_items_rels" ADD CONSTRAINT "pages_blocks_featured_content_block_items_rels_series_fk" FOREIGN KEY ("series_id") REFERENCES "public"."series"("id") ON DELETE cascade ON UPDATE no action; +EXCEPTION + WHEN duplicate_object THEN null; +END $$; + + `); +} + +export async function down({ db, payload, req }: MigrateDownArgs): Promise { + await db.execute(sql` +-- Drop tables in reverse order (child tables first) +DROP TABLE IF EXISTS "pages_blocks_featured_content_block_items_rels"; +DROP TABLE IF EXISTS "pages_blocks_featured_content_block_items_locales"; +DROP TABLE IF EXISTS "pages_blocks_featured_content_block_items"; +DROP TABLE IF EXISTS "pages_blocks_featured_content_block_locales"; +DROP TABLE IF EXISTS "pages_blocks_featured_content_block"; + +DROP TABLE IF EXISTS "pages_blocks_series_detail_block_rels"; +DROP TABLE IF EXISTS "pages_blocks_series_detail_block_locales"; +DROP TABLE IF EXISTS "pages_blocks_series_detail_block"; + +DROP TABLE IF EXISTS "pages_blocks_series_block_locales"; +DROP TABLE IF EXISTS "pages_blocks_series_block"; + +DROP TABLE IF EXISTS "pages_blocks_favorites_block_locales"; +DROP TABLE IF EXISTS "pages_blocks_favorites_block"; + +-- Drop ENUMs +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_items_item_type"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_card_hover"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_card_img_ratio"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_card_bg"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_background_color"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_columns"; +DROP TYPE IF EXISTS "enum_pages_blocks_featured_content_block_layout"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_detail_block_hero_text_align"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_detail_block_hero_height"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_detail_block_layout"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_block_background_color"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_block_columns"; +DROP TYPE IF EXISTS "enum_pages_blocks_series_block_layout"; +DROP TYPE IF EXISTS "enum_pages_blocks_favorites_block_background_color"; +DROP TYPE IF EXISTS "enum_pages_blocks_favorites_block_columns"; +DROP TYPE IF EXISTS "enum_pages_blocks_favorites_block_layout"; +DROP TYPE IF EXISTS "enum_pages_blocks_favorites_block_category"; + `); +} diff --git a/src/migrations/index.ts b/src/migrations/index.ts index 287980f..c4eda59 100644 --- a/src/migrations/index.ts +++ b/src/migrations/index.ts @@ -27,6 +27,7 @@ import * as migration_20260108_231200_add_video_embed_block_tables from './20260 import * as migration_20260108_231400_add_pages_rels_missing_columns from './20260108_231400_add_pages_rels_missing_columns'; import * as migration_20260109_020000_add_blogwoman_collections from './20260109_020000_add_blogwoman_collections'; import * as migration_20260109_023000_add_locations_hours_structured from './20260109_023000_add_locations_hours_structured'; +import * as migration_20260109_030000_add_blogwoman_block_tables from './20260109_030000_add_blogwoman_block_tables'; export const migrations = [ { @@ -174,4 +175,9 @@ export const migrations = [ down: migration_20260109_023000_add_locations_hours_structured.down, name: '20260109_023000_add_locations_hours_structured' }, + { + up: migration_20260109_030000_add_blogwoman_block_tables.up, + down: migration_20260109_030000_add_blogwoman_block_tables.down, + name: '20260109_030000_add_blogwoman_block_tables' + }, ];