Some checks failed
- Phase 5: Translate homepage-modern.html (~90 new locale keys, all hardcoded strings replaced with _() calls for dashboard mock, features, pricing tiers, testimonial sections) - Phase 6: Translate homepage-minimal.html (17 new locale keys for fallback content, features, and CTA sections) - Phase 7: Add multi-language page.title/content support with title_translations and content_translations JSON columns, Alembic migration cms_002, translated title/content resolution in templates, and seed script updates with tt() helper - Phase 8: Complete lb.json audit — fill 6 missing keys (messages, confirmations), also backfill same keys in fr.json and de.json All 4 locale files now have 340 keys with full parity. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1190 lines
49 KiB
Python
Executable File
1190 lines
49 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""
|
|
Create Default Content Pages for All Platforms (CMS)
|
|
|
|
This script creates:
|
|
1. Platform Homepages (slug="home", is_platform_page=True, sections JSON)
|
|
- wizard.lu: multi-product landing (hero + products + cta)
|
|
- omsflow.lu: OMS-focused (hero + features + pricing + cta)
|
|
- rewardflow.lu: loyalty-focused (hero + features + pricing + cta)
|
|
|
|
2. Platform Marketing Pages (is_platform_page=True)
|
|
- about, contact, faq, privacy, terms per platform
|
|
|
|
3. Store Default Pages (is_platform_page=False, store_id=NULL)
|
|
- Generic templates with {{store_name}} placeholders
|
|
- OMS: about, contact, faq, shipping, returns, privacy, terms
|
|
- Loyalty: about, contact, faq, privacy, terms
|
|
|
|
Prerequisites:
|
|
- Database migrations must be applied
|
|
- content_pages table must exist
|
|
- Platforms must exist (run init_production.py first)
|
|
|
|
Usage:
|
|
python scripts/seed/create_default_content_pages.py
|
|
"""
|
|
|
|
import sys
|
|
from datetime import UTC, datetime
|
|
from pathlib import Path
|
|
|
|
# Add project root to path
|
|
project_root = Path(__file__).parent.parent
|
|
sys.path.insert(0, str(project_root))
|
|
|
|
import contextlib
|
|
|
|
from sqlalchemy import select
|
|
from sqlalchemy.orm import Session
|
|
|
|
# Register all models with SQLAlchemy so string-based relationships resolve
|
|
for _mod in [
|
|
"app.modules.billing.models",
|
|
"app.modules.inventory.models",
|
|
"app.modules.cart.models",
|
|
"app.modules.messaging.models",
|
|
"app.modules.loyalty.models",
|
|
"app.modules.catalog.models",
|
|
"app.modules.customers.models",
|
|
"app.modules.orders.models",
|
|
"app.modules.marketplace.models",
|
|
"app.modules.cms.models",
|
|
]:
|
|
with contextlib.suppress(ImportError):
|
|
__import__(_mod)
|
|
|
|
from app.core.database import SessionLocal
|
|
from app.modules.cms.models import ContentPage
|
|
from app.modules.tenancy.models import Platform
|
|
|
|
|
|
# ============================================================================
|
|
# HELPER: TranslatableText dict builder
|
|
# ============================================================================
|
|
|
|
|
|
def t(fr: str, en: str, de: str, lb: str | None = None) -> dict:
|
|
"""Build a TranslatableText-compatible dict."""
|
|
d = {"translations": {"fr": fr, "en": en, "de": de}}
|
|
if lb is not None:
|
|
d["translations"]["lb"] = lb
|
|
return d
|
|
|
|
|
|
def tt(en: str, fr: str, de: str, lb: str | None = None) -> dict:
|
|
"""Build a simple language-keyed dict for title_translations / content_translations."""
|
|
d = {"en": en, "fr": fr, "de": de}
|
|
if lb is not None:
|
|
d["lb"] = lb
|
|
return d
|
|
|
|
|
|
# ============================================================================
|
|
# PLATFORM HOMEPAGE SECTIONS (slug="home", is_platform_page=True)
|
|
# ============================================================================
|
|
|
|
|
|
def _wizard_homepage_sections() -> dict:
|
|
"""Wizard.lu (main) — multi-product landing page."""
|
|
return {
|
|
"hero": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Votre boîte à outils numérique pour votre entreprise",
|
|
"Your Business Digital Toolkit",
|
|
"Ihr digitales Business-Toolkit",
|
|
),
|
|
"subtitle": t(
|
|
"Gestion des commandes, programmes de fidélité et création de sites web — tout ce dont votre entreprise luxembourgeoise a besoin.",
|
|
"Order management, loyalty programs, and website building — everything your Luxembourg business needs.",
|
|
"Bestellverwaltung, Treueprogramme und Website-Erstellung — alles, was Ihr luxemburgisches Unternehmen braucht.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Découvrir nos solutions",
|
|
"Discover Our Solutions",
|
|
"Unsere Lösungen entdecken",
|
|
),
|
|
"url": "#products",
|
|
"style": "primary",
|
|
},
|
|
],
|
|
},
|
|
"products": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Nos solutions",
|
|
"Our Solutions",
|
|
"Unsere Lösungen",
|
|
),
|
|
"subtitle": t(
|
|
"Des outils conçus pour le commerce luxembourgeois",
|
|
"Tools built for Luxembourg commerce",
|
|
"Werkzeuge für den luxemburgischen Handel",
|
|
),
|
|
"products": [
|
|
{
|
|
"icon": "clipboard-list",
|
|
"title": t(
|
|
"Gestion des Commandes (OMS)",
|
|
"Order Management (OMS)",
|
|
"Bestellverwaltung (OMS)",
|
|
),
|
|
"description": t(
|
|
"Synchronisez vos commandes Letzshop, gérez les stocks et générez des factures conformes à la TVA.",
|
|
"Sync your Letzshop orders, manage inventory, and generate VAT-compliant invoices.",
|
|
"Synchronisieren Sie Ihre Letzshop-Bestellungen, verwalten Sie Lagerbestände und erstellen Sie MwSt-konforme Rechnungen.",
|
|
),
|
|
"url": "/platforms/oms/",
|
|
"link_text": t("En savoir plus", "Learn More", "Mehr erfahren"),
|
|
},
|
|
{
|
|
"icon": "heart",
|
|
"title": t(
|
|
"Programme de Fidélité",
|
|
"Loyalty Program",
|
|
"Treueprogramm",
|
|
),
|
|
"description": t(
|
|
"Créez des programmes de fidélité avec points, récompenses et niveaux pour fidéliser vos clients.",
|
|
"Create loyalty programs with points, rewards, and tiers to retain your customers.",
|
|
"Erstellen Sie Treueprogramme mit Punkten, Prämien und Stufen, um Ihre Kunden zu binden.",
|
|
),
|
|
"url": "/platforms/loyalty/",
|
|
"link_text": t("En savoir plus", "Learn More", "Mehr erfahren"),
|
|
},
|
|
{
|
|
"icon": "globe-alt",
|
|
"title": t(
|
|
"Création de Sites Web",
|
|
"Website Builder",
|
|
"Website-Erstellung",
|
|
),
|
|
"description": t(
|
|
"Créez votre présence en ligne avec notre outil de création de sites web intuitif.",
|
|
"Build your online presence with our intuitive website builder.",
|
|
"Erstellen Sie Ihre Online-Präsenz mit unserem intuitiven Website-Baukasten.",
|
|
),
|
|
"url": "",
|
|
"badge": t("Bientôt", "Coming Soon", "Demnächst"),
|
|
},
|
|
],
|
|
},
|
|
"cta": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Prêt à digitaliser votre entreprise ?",
|
|
"Ready to Digitalize Your Business?",
|
|
"Bereit, Ihr Unternehmen zu digitalisieren?",
|
|
),
|
|
"subtitle": t(
|
|
"Rejoignez les entreprises luxembourgeoises qui font confiance à nos solutions.",
|
|
"Join Luxembourg businesses that trust our solutions.",
|
|
"Schließen Sie sich luxemburgischen Unternehmen an, die unseren Lösungen vertrauen.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Commencer maintenant",
|
|
"Get Started Now",
|
|
"Jetzt starten",
|
|
),
|
|
"url": "/signup",
|
|
"style": "primary",
|
|
},
|
|
{
|
|
"text": t(
|
|
"Nous contacter",
|
|
"Contact Us",
|
|
"Kontaktieren Sie uns",
|
|
),
|
|
"url": "/contact",
|
|
"style": "secondary",
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
def _oms_homepage_sections() -> dict:
|
|
"""OMS (omsflow.lu) — order management focused homepage."""
|
|
return {
|
|
"hero": {
|
|
"enabled": True,
|
|
"badge_text": t(
|
|
"Essai gratuit — Aucune carte de crédit requise",
|
|
"Free Trial — No Credit Card Required",
|
|
"Kostenlose Testversion — Keine Kreditkarte erforderlich",
|
|
),
|
|
"title": t(
|
|
"OMS léger pour les vendeurs Letzshop",
|
|
"Lightweight OMS for Letzshop Sellers",
|
|
"Leichtes OMS für Letzshop-Verkäufer",
|
|
),
|
|
"subtitle": t(
|
|
"Gestion des commandes, stocks et facturation conçue pour le e-commerce luxembourgeois. Arrêtez de jongler avec les tableurs. Gérez votre entreprise.",
|
|
"Order management, inventory, and invoicing built for Luxembourg e-commerce. Stop juggling spreadsheets. Start running your business.",
|
|
"Bestellverwaltung, Lager und Rechnungsstellung für den luxemburgischen E-Commerce. Schluss mit Tabellenkalkulationen. Führen Sie Ihr Geschäft.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Essai gratuit",
|
|
"Start Free Trial",
|
|
"Kostenlos testen",
|
|
),
|
|
"url": "/signup",
|
|
"style": "primary",
|
|
},
|
|
{
|
|
"text": t(
|
|
"Trouvez votre boutique Letzshop",
|
|
"Find Your Letzshop Shop",
|
|
"Finden Sie Ihren Letzshop",
|
|
),
|
|
"url": "#find-shop",
|
|
"style": "secondary",
|
|
},
|
|
],
|
|
},
|
|
"features": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Tout ce dont un vendeur Letzshop a besoin",
|
|
"Everything a Letzshop Seller Needs",
|
|
"Alles, was ein Letzshop-Verkäufer braucht",
|
|
),
|
|
"subtitle": t(
|
|
"Les outils opérationnels que Letzshop ne fournit pas",
|
|
"The operational tools Letzshop doesn't provide",
|
|
"Die operativen Tools, die Letzshop nicht bietet",
|
|
),
|
|
"layout": "grid",
|
|
"features": [
|
|
{
|
|
"icon": "refresh",
|
|
"title": t(
|
|
"Synchronisation Letzshop",
|
|
"Letzshop Order Sync",
|
|
"Letzshop-Synchronisierung",
|
|
),
|
|
"description": t(
|
|
"Les commandes se synchronisent automatiquement. Confirmez et ajoutez le suivi depuis Orion.",
|
|
"Orders sync automatically. Confirm and add tracking directly from Orion.",
|
|
"Bestellungen werden automatisch synchronisiert. Bestätigen und Tracking direkt von Orion hinzufügen.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "cube",
|
|
"title": t(
|
|
"Gestion des stocks",
|
|
"Inventory Management",
|
|
"Lagerverwaltung",
|
|
),
|
|
"description": t(
|
|
"Suivez vos stocks en temps réel avec emplacements d'entrepôt et bons de commande.",
|
|
"Track inventory in real-time with warehouse locations and purchase orders.",
|
|
"Verfolgen Sie Lagerbestände in Echtzeit mit Lagerstandorten und Bestellungen.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "document-text",
|
|
"title": t(
|
|
"Facturation TVA UE",
|
|
"EU VAT Invoicing",
|
|
"EU-MwSt-Rechnungen",
|
|
),
|
|
"description": t(
|
|
"Générez des factures PDF conformes avec la TVA correcte pour tout pays UE.",
|
|
"Generate compliant PDF invoices with correct VAT for any EU country.",
|
|
"Erstellen Sie konforme PDF-Rechnungen mit korrekter MwSt für jedes EU-Land.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "users",
|
|
"title": t(
|
|
"Données clients",
|
|
"Customer Data",
|
|
"Kundendaten",
|
|
),
|
|
"description": t(
|
|
"Possédez vos données clients. Exportez pour le marketing et la fidélisation.",
|
|
"Own your customer data. Export for marketing and loyalty building.",
|
|
"Besitzen Sie Ihre Kundendaten. Exportieren Sie für Marketing und Kundenbindung.",
|
|
),
|
|
},
|
|
],
|
|
},
|
|
"pricing": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Tarification simple et transparente",
|
|
"Simple, Transparent Pricing",
|
|
"Einfache, transparente Preise",
|
|
),
|
|
"subtitle": t(
|
|
"Choisissez le plan adapté à votre entreprise. Tous les plans incluent un essai gratuit.",
|
|
"Choose the plan that fits your business. All plans include a free trial.",
|
|
"Wählen Sie den Plan, der zu Ihrem Unternehmen passt. Alle Pläne beinhalten eine kostenlose Testversion.",
|
|
),
|
|
"use_subscription_tiers": True,
|
|
"monthly_label": t("Mensuel", "Monthly", "Monatlich"),
|
|
"annual_label": t("Annuel", "Annual", "Jährlich"),
|
|
"save_text": t("Économisez 2 mois !", "Save 2 months!", "Sparen Sie 2 Monate!"),
|
|
"popular_badge": t("LE PLUS POPULAIRE", "MOST POPULAR", "AM BELIEBTESTEN"),
|
|
"cta_text": t("Essai gratuit", "Start Free Trial", "Kostenlos testen"),
|
|
"per_month_label": t("/mois", "/month", "/Monat"),
|
|
"per_year_label": t("/an", "/year", "/Jahr"),
|
|
"coming_soon_text": t(
|
|
"Plans tarifaires bientôt disponibles",
|
|
"Pricing plans coming soon",
|
|
"Preispläne demnächst verfügbar",
|
|
),
|
|
},
|
|
"cta": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Prêt à optimiser vos commandes ?",
|
|
"Ready to Streamline Your Orders?",
|
|
"Bereit, Ihre Bestellungen zu optimieren?",
|
|
),
|
|
"subtitle": t(
|
|
"Rejoignez les vendeurs Letzshop qui font confiance à Orion pour leur gestion de commandes.",
|
|
"Join Letzshop stores who trust Orion for their order management.",
|
|
"Schließen Sie sich Letzshop-Händlern an, die Orion für ihre Bestellverwaltung vertrauen.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Essai gratuit",
|
|
"Start Free Trial",
|
|
"Kostenlos testen",
|
|
),
|
|
"url": "/signup",
|
|
"style": "primary",
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
def _loyalty_homepage_sections() -> dict:
|
|
"""Loyalty (rewardflow.lu) — loyalty program focused homepage."""
|
|
return {
|
|
"hero": {
|
|
"enabled": True,
|
|
"badge_text": t(
|
|
"Essai gratuit — Aucune carte de crédit requise",
|
|
"Free Trial — No Credit Card Required",
|
|
"Kostenlose Testversion — Keine Kreditkarte erforderlich",
|
|
),
|
|
"title": t(
|
|
"Fidélisation client simplifiée",
|
|
"Customer Loyalty Made Simple",
|
|
"Kundentreue leicht gemacht",
|
|
),
|
|
"subtitle": t(
|
|
"Créez des programmes de fidélité engageants avec points, récompenses et niveaux. Conçu pour les commerces luxembourgeois.",
|
|
"Create engaging loyalty programs with points, rewards, and tiers. Built for Luxembourg businesses.",
|
|
"Erstellen Sie ansprechende Treueprogramme mit Punkten, Prämien und Stufen. Für luxemburgische Unternehmen.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Essai gratuit",
|
|
"Start Free Trial",
|
|
"Kostenlos testen",
|
|
),
|
|
"url": "/signup",
|
|
"style": "primary",
|
|
},
|
|
{
|
|
"text": t(
|
|
"Voir les fonctionnalités",
|
|
"See Features",
|
|
"Funktionen ansehen",
|
|
),
|
|
"url": "#features",
|
|
"style": "secondary",
|
|
},
|
|
],
|
|
},
|
|
"features": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Tout pour fidéliser vos clients",
|
|
"Everything to Build Customer Loyalty",
|
|
"Alles für die Kundenbindung",
|
|
),
|
|
"subtitle": t(
|
|
"Des outils puissants pour créer et gérer vos programmes de fidélité",
|
|
"Powerful tools to create and manage your loyalty programs",
|
|
"Leistungsstarke Tools zum Erstellen und Verwalten Ihrer Treueprogramme",
|
|
),
|
|
"layout": "grid",
|
|
"features": [
|
|
{
|
|
"icon": "star",
|
|
"title": t(
|
|
"Système de points",
|
|
"Points System",
|
|
"Punktesystem",
|
|
),
|
|
"description": t(
|
|
"Attribuez des points pour chaque achat. Règles flexibles et multiplicateurs personnalisables.",
|
|
"Award points for every purchase. Flexible rules and customizable multipliers.",
|
|
"Vergeben Sie Punkte für jeden Einkauf. Flexible Regeln und anpassbare Multiplikatoren.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "gift",
|
|
"title": t(
|
|
"Catalogue de récompenses",
|
|
"Rewards Catalog",
|
|
"Prämienkatalog",
|
|
),
|
|
"description": t(
|
|
"Créez un catalogue de récompenses attrayant. Produits, remises ou expériences exclusives.",
|
|
"Create an attractive rewards catalog. Products, discounts, or exclusive experiences.",
|
|
"Erstellen Sie einen attraktiven Prämienkatalog. Produkte, Rabatte oder exklusive Erlebnisse.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "trending-up",
|
|
"title": t(
|
|
"Gestion des niveaux",
|
|
"Tier Management",
|
|
"Stufenverwaltung",
|
|
),
|
|
"description": t(
|
|
"Bronze, Argent, Or — motivez vos clients avec des niveaux de fidélité progressifs.",
|
|
"Bronze, Silver, Gold — motivate customers with progressive loyalty tiers.",
|
|
"Bronze, Silber, Gold — motivieren Sie Kunden mit progressiven Treuestufen.",
|
|
),
|
|
},
|
|
{
|
|
"icon": "chart-bar",
|
|
"title": t(
|
|
"Analytique avancée",
|
|
"Advanced Analytics",
|
|
"Erweiterte Analytik",
|
|
),
|
|
"description": t(
|
|
"Suivez l'engagement, les taux de rétention et le ROI de vos programmes.",
|
|
"Track engagement, retention rates, and program ROI.",
|
|
"Verfolgen Sie Engagement, Bindungsraten und Programm-ROI.",
|
|
),
|
|
},
|
|
],
|
|
},
|
|
"pricing": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Tarification simple et transparente",
|
|
"Simple, Transparent Pricing",
|
|
"Einfache, transparente Preise",
|
|
),
|
|
"subtitle": t(
|
|
"Choisissez le plan adapté à votre programme de fidélité.",
|
|
"Choose the plan that fits your loyalty program.",
|
|
"Wählen Sie den Plan, der zu Ihrem Treueprogramm passt.",
|
|
),
|
|
"use_subscription_tiers": True,
|
|
"monthly_label": t("Mensuel", "Monthly", "Monatlich"),
|
|
"annual_label": t("Annuel", "Annual", "Jährlich"),
|
|
"save_text": t("Économisez 2 mois !", "Save 2 months!", "Sparen Sie 2 Monate!"),
|
|
"popular_badge": t("LE PLUS POPULAIRE", "MOST POPULAR", "AM BELIEBTESTEN"),
|
|
"cta_text": t("Essai gratuit", "Start Free Trial", "Kostenlos testen"),
|
|
"per_month_label": t("/mois", "/month", "/Monat"),
|
|
"per_year_label": t("/an", "/year", "/Jahr"),
|
|
"coming_soon_text": t(
|
|
"Plans tarifaires bientôt disponibles",
|
|
"Pricing plans coming soon",
|
|
"Preispläne demnächst verfügbar",
|
|
),
|
|
},
|
|
"cta": {
|
|
"enabled": True,
|
|
"title": t(
|
|
"Prêt à fidéliser vos clients ?",
|
|
"Ready to Build Customer Loyalty?",
|
|
"Bereit, Kundentreue aufzubauen?",
|
|
),
|
|
"subtitle": t(
|
|
"Commencez votre programme de fidélité dès aujourd'hui. Essai gratuit, sans engagement.",
|
|
"Start your loyalty program today. Free trial, no commitment.",
|
|
"Starten Sie Ihr Treueprogramm noch heute. Kostenlose Testversion, ohne Verpflichtung.",
|
|
),
|
|
"background_type": "gradient",
|
|
"buttons": [
|
|
{
|
|
"text": t(
|
|
"Essai gratuit",
|
|
"Start Free Trial",
|
|
"Kostenlos testen",
|
|
),
|
|
"url": "/signup",
|
|
"style": "primary",
|
|
},
|
|
],
|
|
},
|
|
}
|
|
|
|
|
|
HOMEPAGE_SECTIONS = {
|
|
"main": _wizard_homepage_sections,
|
|
"oms": _oms_homepage_sections,
|
|
"loyalty": _loyalty_homepage_sections,
|
|
}
|
|
|
|
|
|
# ============================================================================
|
|
# PLATFORM MARKETING PAGES (is_platform_page=True)
|
|
# ============================================================================
|
|
|
|
|
|
def _get_platform_pages(platform_code: str) -> list[dict]:
|
|
"""Get platform marketing pages for a given platform code."""
|
|
|
|
if platform_code == "main":
|
|
return [
|
|
{
|
|
"slug": "about",
|
|
"title": "About Wizard",
|
|
"title_translations": tt("About Wizard", "À propos de Wizard", "Über Wizard", "Iwwer Wizard"),
|
|
"content": """<div class="prose-content">
|
|
<h2>About Wizard</h2>
|
|
<p>Wizard is the digital toolkit for Luxembourg businesses. We provide integrated solutions for order management, customer loyalty, and online presence.</p>
|
|
<h3>Our Mission</h3>
|
|
<p>To empower Luxembourg businesses with modern, easy-to-use digital tools that help them grow and thrive in the digital age.</p>
|
|
<h3>Our Solutions</h3>
|
|
<ul>
|
|
<li><strong>OMS (omsflow.lu):</strong> Order management, inventory, and invoicing for Letzshop sellers</li>
|
|
<li><strong>Loyalty (rewardflow.lu):</strong> Customer loyalty programs with points, rewards, and tiers</li>
|
|
<li><strong>Website Builder:</strong> Coming soon — build your online presence</li>
|
|
</ul>
|
|
<h3>Built in Luxembourg</h3>
|
|
<p>We understand the unique needs of Luxembourg commerce — from multilingual support (FR/DE/EN) to EU VAT compliance.</p>
|
|
</div>""",
|
|
"meta_description": "Wizard — the digital toolkit for Luxembourg businesses. Order management, loyalty programs, and more.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 1,
|
|
},
|
|
{
|
|
"slug": "contact",
|
|
"title": "Contact Us",
|
|
"title_translations": tt("Contact Us", "Contactez-nous", "Kontakt", "Kontakt"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Contact Wizard</h2>
|
|
<p>We'd love to hear from you. Get in touch with our team.</p>
|
|
<h3>General Inquiries</h3>
|
|
<ul>
|
|
<li><strong>Email:</strong> info@wizard.lu</li>
|
|
</ul>
|
|
<h3>Sales</h3>
|
|
<p>Interested in our solutions for your business?</p>
|
|
<ul>
|
|
<li><strong>Email:</strong> sales@wizard.lu</li>
|
|
</ul>
|
|
<h3>Support</h3>
|
|
<p>Already a customer? Our support team is here to help.</p>
|
|
<ul>
|
|
<li><strong>Email:</strong> support@wizard.lu</li>
|
|
</ul>
|
|
<h3>Office</h3>
|
|
<p>Luxembourg</p>
|
|
</div>""",
|
|
"meta_description": "Contact the Wizard team for inquiries about our business solutions.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 2,
|
|
},
|
|
{
|
|
"slug": "faq",
|
|
"title": "FAQ",
|
|
"title_translations": tt("FAQ", "FAQ", "FAQ", "FAQ"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Frequently Asked Questions</h2>
|
|
<h3>General</h3>
|
|
<h4>What is Wizard?</h4>
|
|
<p>Wizard is a suite of digital tools for Luxembourg businesses, including order management (OMS), customer loyalty programs, and website building.</p>
|
|
<h4>Who is Wizard for?</h4>
|
|
<p>Wizard is designed for Luxembourg businesses of all sizes — from individual Letzshop sellers to multi-store retailers.</p>
|
|
<h3>Pricing & Billing</h3>
|
|
<h4>Is there a free trial?</h4>
|
|
<p>Yes! All our solutions offer a free trial period. No credit card required to start.</p>
|
|
<h4>Can I switch plans?</h4>
|
|
<p>Yes, you can upgrade or downgrade your plan at any time.</p>
|
|
<h3>Technical</h3>
|
|
<h4>What languages are supported?</h4>
|
|
<p>Our platform supports French, German, and English — the three official languages of Luxembourg.</p>
|
|
<h4>Is my data secure?</h4>
|
|
<p>Yes. We use industry-standard encryption and follow GDPR regulations for data protection.</p>
|
|
</div>""",
|
|
"meta_description": "Frequently asked questions about Wizard solutions for Luxembourg businesses.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 3,
|
|
},
|
|
]
|
|
|
|
if platform_code == "oms":
|
|
return [
|
|
{
|
|
"slug": "about",
|
|
"title": "About OMS",
|
|
"title_translations": tt("About OMS", "À propos d'OMS", "Über OMS", "Iwwer OMS"),
|
|
"content": """<div class="prose-content">
|
|
<h2>About OMS by Wizard</h2>
|
|
<p>OMS (omsflow.lu) is a lightweight order management system built specifically for Letzshop sellers in Luxembourg.</p>
|
|
<h3>Why OMS?</h3>
|
|
<p>Letzshop is a great marketplace, but it doesn't give sellers the back-office tools they need. OMS fills that gap with:</p>
|
|
<ul>
|
|
<li><strong>Automatic order sync</strong> from Letzshop</li>
|
|
<li><strong>Inventory management</strong> with warehouse locations</li>
|
|
<li><strong>EU VAT invoicing</strong> with correct rates per country</li>
|
|
<li><strong>Customer data ownership</strong> for marketing and retention</li>
|
|
</ul>
|
|
<h3>Built for Luxembourg</h3>
|
|
<p>From multilingual support to Luxembourg VAT compliance, OMS is designed for the local market.</p>
|
|
</div>""",
|
|
"meta_description": "OMS — lightweight order management for Letzshop sellers. Manage orders, inventory, and invoicing.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 1,
|
|
},
|
|
{
|
|
"slug": "contact",
|
|
"title": "Contact OMS Support",
|
|
"title_translations": tt("Contact OMS Support", "Contacter le support OMS", "OMS Support kontaktieren", "OMS Support kontaktéieren"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Contact OMS Support</h2>
|
|
<p>Need help with your order management? We're here for you.</p>
|
|
<h3>Support</h3>
|
|
<ul>
|
|
<li><strong>Email:</strong> support@omsflow.lu</li>
|
|
</ul>
|
|
<h3>Sales</h3>
|
|
<p>Interested in OMS for your Letzshop store?</p>
|
|
<ul>
|
|
<li><strong>Email:</strong> sales@omsflow.lu</li>
|
|
</ul>
|
|
</div>""",
|
|
"meta_description": "Contact the OMS support team for help with order management.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 2,
|
|
},
|
|
{
|
|
"slug": "faq",
|
|
"title": "OMS FAQ",
|
|
"title_translations": tt("OMS FAQ", "FAQ OMS", "OMS FAQ", "OMS FAQ"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Frequently Asked Questions</h2>
|
|
<h4>Do I need a Letzshop account?</h4>
|
|
<p>While OMS works best with Letzshop integration, you can also use it as a standalone order management tool.</p>
|
|
<h4>How does Letzshop sync work?</h4>
|
|
<p>Enter your Letzshop API credentials and orders sync automatically. Setup takes about 2 minutes.</p>
|
|
<h4>Is EU VAT invoicing included?</h4>
|
|
<p>Yes, EU VAT invoicing is available on Professional and Enterprise plans. Luxembourg VAT is available on all plans.</p>
|
|
<h4>Can I export my data?</h4>
|
|
<p>Yes, you can export customers, orders, and invoices at any time.</p>
|
|
<h4>Is there a free trial?</h4>
|
|
<p>Yes! Start with a free trial, no credit card required.</p>
|
|
</div>""",
|
|
"meta_description": "Frequently asked questions about OMS order management for Letzshop sellers.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 3,
|
|
},
|
|
]
|
|
|
|
if platform_code == "loyalty":
|
|
return [
|
|
{
|
|
"slug": "about",
|
|
"title": "About Loyalty",
|
|
"title_translations": tt("About Loyalty", "À propos de Loyalty", "Über Loyalty", "Iwwer Loyalty"),
|
|
"content": """<div class="prose-content">
|
|
<h2>About Loyalty by Wizard</h2>
|
|
<p>Loyalty (rewardflow.lu) helps Luxembourg businesses create engaging customer loyalty programs.</p>
|
|
<h3>What We Offer</h3>
|
|
<ul>
|
|
<li><strong>Points system:</strong> Award points for purchases with flexible rules</li>
|
|
<li><strong>Rewards catalog:</strong> Products, discounts, or exclusive experiences</li>
|
|
<li><strong>Tier management:</strong> Bronze, Silver, Gold — progressive loyalty levels</li>
|
|
<li><strong>Analytics:</strong> Track engagement, retention, and program ROI</li>
|
|
</ul>
|
|
<h3>Built for Luxembourg</h3>
|
|
<p>Multilingual support (FR/DE/EN) and local business features out of the box.</p>
|
|
</div>""",
|
|
"meta_description": "Loyalty — customer loyalty programs for Luxembourg businesses. Points, rewards, and tiers.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 1,
|
|
},
|
|
{
|
|
"slug": "contact",
|
|
"title": "Contact Loyalty Support",
|
|
"title_translations": tt("Contact Loyalty Support", "Contacter le support Loyalty", "Loyalty Support kontaktieren", "Loyalty Support kontaktéieren"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Contact Loyalty Support</h2>
|
|
<p>Need help with your loyalty program? We're here for you.</p>
|
|
<h3>Support</h3>
|
|
<ul>
|
|
<li><strong>Email:</strong> support@rewardflow.lu</li>
|
|
</ul>
|
|
<h3>Sales</h3>
|
|
<p>Interested in a loyalty program for your business?</p>
|
|
<ul>
|
|
<li><strong>Email:</strong> sales@rewardflow.lu</li>
|
|
</ul>
|
|
</div>""",
|
|
"meta_description": "Contact the Loyalty support team for help with your loyalty program.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 2,
|
|
},
|
|
{
|
|
"slug": "faq",
|
|
"title": "Loyalty FAQ",
|
|
"title_translations": tt("Loyalty FAQ", "FAQ Loyalty", "Loyalty FAQ", "Loyalty FAQ"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Frequently Asked Questions</h2>
|
|
<h4>How do loyalty points work?</h4>
|
|
<p>You define the rules — for example, 1 point per euro spent. Points can be redeemed for rewards from your catalog.</p>
|
|
<h4>Can I customize the loyalty tiers?</h4>
|
|
<p>Yes! You can create custom tiers with different names, thresholds, and benefits.</p>
|
|
<h4>How do customers check their points?</h4>
|
|
<p>Customers can check their balance through a personalized loyalty page or at your point of sale.</p>
|
|
<h4>Is there a free trial?</h4>
|
|
<p>Yes! Start with a free trial, no credit card required.</p>
|
|
</div>""",
|
|
"meta_description": "Frequently asked questions about Loyalty customer loyalty programs.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 3,
|
|
},
|
|
]
|
|
|
|
return []
|
|
|
|
|
|
# Shared legal pages (same content for all platforms)
|
|
SHARED_PLATFORM_PAGES = [
|
|
{
|
|
"slug": "privacy",
|
|
"title": "Privacy Policy",
|
|
"title_translations": tt("Privacy Policy", "Politique de confidentialité", "Datenschutzrichtlinie", "Dateschutzrichtlinn"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Privacy Policy</h2>
|
|
<p><em>Last Updated: February 2026</em></p>
|
|
<h3>Information We Collect</h3>
|
|
<p>We collect information you provide directly:</p>
|
|
<ul>
|
|
<li>Name, email address, and contact information</li>
|
|
<li>Business information (for merchants)</li>
|
|
<li>Payment information (processed securely by our payment processor)</li>
|
|
</ul>
|
|
<h3>How We Use Your Information</h3>
|
|
<p>We use your information to provide and improve our services, process payments, communicate with you, and comply with legal obligations.</p>
|
|
<h3>Data Protection (GDPR)</h3>
|
|
<p>We comply with the EU General Data Protection Regulation. You have the right to access, correct, delete, or export your personal data at any time.</p>
|
|
<h3>Contact</h3>
|
|
<p>For privacy-related questions, contact privacy@wizard.lu</p>
|
|
</div>""",
|
|
"meta_description": "Privacy policy — how we collect, use, and protect your personal information.",
|
|
"show_in_footer": False,
|
|
"show_in_header": False,
|
|
"show_in_legal": True,
|
|
"display_order": 10,
|
|
},
|
|
{
|
|
"slug": "terms",
|
|
"title": "Terms of Service",
|
|
"title_translations": tt("Terms of Service", "Conditions d'utilisation", "Nutzungsbedingungen", "Notzungsbedingungen"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Terms of Service</h2>
|
|
<p><em>Last Updated: February 2026</em></p>
|
|
<h3>1. Acceptance of Terms</h3>
|
|
<p>By accessing and using this platform, you accept and agree to be bound by these Terms of Service.</p>
|
|
<h3>2. Services</h3>
|
|
<p>We provide digital business tools including order management, loyalty programs, and website building services.</p>
|
|
<h3>3. Account</h3>
|
|
<p>You must provide accurate information and maintain the security of your account.</p>
|
|
<h3>4. Payments</h3>
|
|
<p>Subscription fees are billed monthly or annually. You may cancel at any time.</p>
|
|
<h3>5. Data & Privacy</h3>
|
|
<p>Your use of our services is also governed by our Privacy Policy.</p>
|
|
<h3>6. Governing Law</h3>
|
|
<p>These terms are governed by the laws of Luxembourg.</p>
|
|
<h3>Contact</h3>
|
|
<p>For questions about these terms, contact legal@wizard.lu</p>
|
|
</div>""",
|
|
"meta_description": "Terms of service governing the use of our platform.",
|
|
"show_in_footer": False,
|
|
"show_in_header": False,
|
|
"show_in_legal": True,
|
|
"display_order": 11,
|
|
},
|
|
]
|
|
|
|
|
|
# ============================================================================
|
|
# STORE DEFAULT PAGES (is_platform_page=False, store_id=NULL)
|
|
# Uses {{store_name}}, {{store_email}}, {{store_phone}} placeholders
|
|
# ============================================================================
|
|
|
|
|
|
STORE_DEFAULTS_COMMON = [
|
|
{
|
|
"slug": "about",
|
|
"title": "About Us",
|
|
"title_translations": tt("About Us", "À propos", "Über uns", "Iwwer eis"),
|
|
"content": """<div class="prose-content">
|
|
<h2>About {{store_name}}</h2>
|
|
<p>Welcome to {{store_name}}. We are committed to providing you with quality products and excellent service.</p>
|
|
<h3>Our Story</h3>
|
|
<p>{{store_name}} was founded with a simple mission: to deliver exceptional value to our customers.</p>
|
|
<h3>Contact Us</h3>
|
|
<p>Have questions? We'd love to hear from you.</p>
|
|
<ul>
|
|
<li><strong>Email:</strong> {{store_email}}</li>
|
|
<li><strong>Phone:</strong> {{store_phone}}</li>
|
|
</ul>
|
|
</div>""",
|
|
"meta_description": "Learn about {{store_name}} — our story, values, and commitment to quality.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 1,
|
|
},
|
|
{
|
|
"slug": "contact",
|
|
"title": "Contact Us",
|
|
"title_translations": tt("Contact Us", "Contactez-nous", "Kontakt", "Kontakt"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Contact {{store_name}}</h2>
|
|
<p>We're here to help! Reach out to us.</p>
|
|
<h3>Get in Touch</h3>
|
|
<ul>
|
|
<li><strong>Email:</strong> {{store_email}}</li>
|
|
<li><strong>Phone:</strong> {{store_phone}}</li>
|
|
</ul>
|
|
<p>We typically respond within 24 hours during business days.</p>
|
|
</div>""",
|
|
"meta_description": "Contact {{store_name}} for questions, support, or inquiries.",
|
|
"show_in_footer": True,
|
|
"show_in_header": True,
|
|
"display_order": 2,
|
|
},
|
|
{
|
|
"slug": "faq",
|
|
"title": "FAQ",
|
|
"title_translations": tt("FAQ", "FAQ", "FAQ", "FAQ"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Frequently Asked Questions</h2>
|
|
<h4>How can I contact you?</h4>
|
|
<p>You can reach us at {{store_email}} or call {{store_phone}}.</p>
|
|
<h4>What are your business hours?</h4>
|
|
<p>We are available during regular business hours, Monday through Friday.</p>
|
|
<h4>Do you have a physical location?</h4>
|
|
<p>Please contact us for information about our location and visiting hours.</p>
|
|
</div>""",
|
|
"meta_description": "Frequently asked questions about {{store_name}}.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 3,
|
|
},
|
|
{
|
|
"slug": "privacy",
|
|
"title": "Privacy Policy",
|
|
"title_translations": tt("Privacy Policy", "Politique de confidentialité", "Datenschutzrichtlinie", "Dateschutzrichtlinn"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Privacy Policy</h2>
|
|
<p>{{store_name}} is committed to protecting your personal information.</p>
|
|
<h3>Information We Collect</h3>
|
|
<p>We collect information necessary to process your orders and provide customer service.</p>
|
|
<h3>Your Rights</h3>
|
|
<p>Under GDPR, you have the right to access, correct, or delete your personal data. Contact us at {{store_email}}.</p>
|
|
</div>""",
|
|
"meta_description": "Privacy policy for {{store_name}}.",
|
|
"show_in_footer": False,
|
|
"show_in_header": False,
|
|
"show_in_legal": True,
|
|
"display_order": 10,
|
|
},
|
|
{
|
|
"slug": "terms",
|
|
"title": "Terms of Service",
|
|
"title_translations": tt("Terms of Service", "Conditions d'utilisation", "Nutzungsbedingungen", "Notzungsbedingungen"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Terms of Service</h2>
|
|
<p>By using the services provided by {{store_name}}, you agree to these terms.</p>
|
|
<h3>Orders</h3>
|
|
<p>All orders are subject to availability and confirmation.</p>
|
|
<h3>Contact</h3>
|
|
<p>For questions about these terms, contact us at {{store_email}}.</p>
|
|
</div>""",
|
|
"meta_description": "Terms of service for {{store_name}}.",
|
|
"show_in_footer": False,
|
|
"show_in_header": False,
|
|
"show_in_legal": True,
|
|
"display_order": 11,
|
|
},
|
|
]
|
|
|
|
# OMS-specific store defaults (shipping/returns)
|
|
STORE_DEFAULTS_OMS_EXTRA = [
|
|
{
|
|
"slug": "shipping",
|
|
"title": "Shipping Policy",
|
|
"title_translations": tt("Shipping Policy", "Politique de livraison", "Versandrichtlinie", "Versandrichtlinn"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Shipping Policy</h2>
|
|
<h3>Shipping Methods</h3>
|
|
<p>{{store_name}} offers multiple shipping options:</p>
|
|
<ul>
|
|
<li><strong>Standard Shipping:</strong> 3-7 business days</li>
|
|
<li><strong>Express Shipping:</strong> 1-3 business days</li>
|
|
</ul>
|
|
<h3>Shipping Costs</h3>
|
|
<p>Shipping costs are calculated based on weight and destination at checkout.</p>
|
|
<h3>Tracking</h3>
|
|
<p>You will receive a tracking number by email once your order ships.</p>
|
|
<h3>Questions?</h3>
|
|
<p>Contact us at {{store_email}} for shipping inquiries.</p>
|
|
</div>""",
|
|
"meta_description": "Shipping policy for {{store_name}} — methods, costs, and delivery times.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 4,
|
|
},
|
|
{
|
|
"slug": "returns",
|
|
"title": "Return & Refund Policy",
|
|
"title_translations": tt("Return & Refund Policy", "Politique de retour et remboursement", "Rückgabe- und Erstattungsrichtlinie", "Retour- a Rembourséierungsrichtlinn"),
|
|
"content": """<div class="prose-content">
|
|
<h2>Return & Refund Policy</h2>
|
|
<h3>Returns</h3>
|
|
<p>{{store_name}} accepts returns within 14 days of delivery, in accordance with Luxembourg consumer protection law.</p>
|
|
<h3>How to Return</h3>
|
|
<ol>
|
|
<li>Contact us at {{store_email}} to initiate a return</li>
|
|
<li>Pack the item in its original condition</li>
|
|
<li>Ship using the provided return instructions</li>
|
|
</ol>
|
|
<h3>Refunds</h3>
|
|
<p>Refunds are processed within 14 days of receiving the returned item, back to the original payment method.</p>
|
|
<h3>Damaged Items</h3>
|
|
<p>If you receive a damaged item, contact us immediately at {{store_email}} with photos.</p>
|
|
</div>""",
|
|
"meta_description": "Return and refund policy for {{store_name}}.",
|
|
"show_in_footer": True,
|
|
"show_in_header": False,
|
|
"display_order": 5,
|
|
},
|
|
]
|
|
|
|
|
|
# ============================================================================
|
|
# SCRIPT FUNCTIONS
|
|
# ============================================================================
|
|
|
|
|
|
def _page_exists(db: Session, platform_id: int, slug: str, *, is_platform_page: bool) -> bool:
|
|
"""Check if a page already exists for the given platform, slug, and type."""
|
|
existing = db.execute(
|
|
select(ContentPage).where(
|
|
ContentPage.platform_id == platform_id,
|
|
ContentPage.store_id.is_(None),
|
|
ContentPage.slug == slug,
|
|
ContentPage.is_platform_page == is_platform_page,
|
|
)
|
|
).scalar_one_or_none()
|
|
return existing is not None
|
|
|
|
|
|
def _create_page(
|
|
db: Session,
|
|
platform_id: int,
|
|
page_data: dict,
|
|
*,
|
|
is_platform_page: bool = False,
|
|
sections: dict | None = None,
|
|
) -> bool:
|
|
"""Create a single content page. Returns True if created, False if skipped."""
|
|
slug = page_data["slug"]
|
|
title = page_data["title"]
|
|
|
|
if _page_exists(db, platform_id, slug, is_platform_page=is_platform_page):
|
|
print(f" Skipped: {title} (/{slug}) - already exists")
|
|
return False
|
|
|
|
page = ContentPage(
|
|
platform_id=platform_id,
|
|
store_id=None,
|
|
slug=slug,
|
|
title=title,
|
|
title_translations=page_data.get("title_translations"),
|
|
content=page_data.get("content", ""),
|
|
content_translations=page_data.get("content_translations"),
|
|
content_format="html",
|
|
template=page_data.get("template", "default"),
|
|
sections=sections,
|
|
meta_description=page_data.get("meta_description", ""),
|
|
meta_keywords=page_data.get("meta_keywords", ""),
|
|
is_platform_page=is_platform_page,
|
|
is_published=True,
|
|
published_at=datetime.now(UTC),
|
|
show_in_footer=page_data.get("show_in_footer", False),
|
|
show_in_header=page_data.get("show_in_header", False),
|
|
show_in_legal=page_data.get("show_in_legal", False),
|
|
display_order=page_data.get("display_order", 0),
|
|
created_at=datetime.now(UTC),
|
|
updated_at=datetime.now(UTC),
|
|
)
|
|
|
|
db.add(page)
|
|
tier_label = "platform" if is_platform_page else "store-default"
|
|
print(f" Created [{tier_label}]: {title} (/{slug})")
|
|
return True
|
|
|
|
|
|
def create_default_pages(db: Session) -> None:
|
|
"""
|
|
Create all default content pages for all platforms.
|
|
|
|
This function is idempotent — it skips pages that already exist.
|
|
"""
|
|
print("\n" + "=" * 70)
|
|
print("Creating Default Content Pages (CMS)")
|
|
print("=" * 70 + "\n")
|
|
|
|
# Load all platforms
|
|
platforms = db.execute(select(Platform)).scalars().all()
|
|
if not platforms:
|
|
print(" No platforms found. Run init_production.py first.")
|
|
return
|
|
|
|
total_created = 0
|
|
total_skipped = 0
|
|
|
|
for platform in platforms:
|
|
print(f"\n Platform: {platform.name} (code={platform.code})")
|
|
created_count = 0
|
|
skipped_count = 0
|
|
|
|
# ------------------------------------------------------------------
|
|
# 1. Platform Homepage (slug="home", is_platform_page=True, sections)
|
|
# ------------------------------------------------------------------
|
|
sections_fn = HOMEPAGE_SECTIONS.get(platform.code)
|
|
if sections_fn:
|
|
sections_data = sections_fn()
|
|
homepage_data = {
|
|
"slug": "home",
|
|
"title": f"{platform.name} - Home",
|
|
"content": "",
|
|
"meta_description": f"{platform.name} — digital tools for Luxembourg businesses.",
|
|
"show_in_footer": False,
|
|
"show_in_header": False,
|
|
"display_order": 0,
|
|
}
|
|
if _create_page(
|
|
db, platform.id, homepage_data,
|
|
is_platform_page=True, sections=sections_data,
|
|
):
|
|
created_count += 1
|
|
else:
|
|
skipped_count += 1
|
|
else:
|
|
print(f" Warning: No homepage sections defined for platform '{platform.code}'")
|
|
|
|
# ------------------------------------------------------------------
|
|
# 2. Platform Marketing Pages (is_platform_page=True)
|
|
# ------------------------------------------------------------------
|
|
platform_pages = _get_platform_pages(platform.code)
|
|
for page_data in platform_pages:
|
|
if _create_page(db, platform.id, page_data, is_platform_page=True):
|
|
created_count += 1
|
|
else:
|
|
skipped_count += 1
|
|
|
|
# Shared legal pages (privacy, terms) for all platforms
|
|
for page_data in SHARED_PLATFORM_PAGES:
|
|
if _create_page(db, platform.id, page_data, is_platform_page=True):
|
|
created_count += 1
|
|
else:
|
|
skipped_count += 1
|
|
|
|
# ------------------------------------------------------------------
|
|
# 3. Store Default Pages (is_platform_page=False, store_id=NULL)
|
|
# Only for platforms that host stores (not wizard.lu main)
|
|
# ------------------------------------------------------------------
|
|
if platform.code != "main":
|
|
for page_data in STORE_DEFAULTS_COMMON:
|
|
if _create_page(db, platform.id, page_data, is_platform_page=False):
|
|
created_count += 1
|
|
else:
|
|
skipped_count += 1
|
|
|
|
# OMS-specific: shipping + returns
|
|
if platform.code == "oms":
|
|
for page_data in STORE_DEFAULTS_OMS_EXTRA:
|
|
if _create_page(db, platform.id, page_data, is_platform_page=False):
|
|
created_count += 1
|
|
else:
|
|
skipped_count += 1
|
|
|
|
print(f" --- {created_count} created, {skipped_count} skipped")
|
|
|
|
total_created += created_count
|
|
total_skipped += skipped_count
|
|
|
|
db.commit()
|
|
|
|
print("\n" + "=" * 70)
|
|
print("Summary:")
|
|
print(f" Platforms: {len(platforms)}")
|
|
print(f" Created: {total_created} pages")
|
|
print(f" Skipped: {total_skipped} pages (already exist)")
|
|
print(f" Total: {total_created + total_skipped} pages")
|
|
print("=" * 70 + "\n")
|
|
|
|
if total_created > 0:
|
|
print("Default content pages created successfully!\n")
|
|
else:
|
|
print("All default pages already exist. No changes made.\n")
|
|
|
|
|
|
# ============================================================================
|
|
# MAIN EXECUTION
|
|
# ============================================================================
|
|
|
|
|
|
def main():
|
|
"""Main execution function."""
|
|
print("\nStarting Default Content Pages Creation Script...\n")
|
|
|
|
db = SessionLocal()
|
|
try:
|
|
create_default_pages(db)
|
|
print("Script completed successfully!\n")
|
|
except Exception as e:
|
|
print(f"\nError: {e}\n")
|
|
db.rollback()
|
|
raise
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|