diff --git a/app/modules/cms/routes/pages/platform.py b/app/modules/cms/routes/pages/platform.py index d9929a1a..50939617 100644 --- a/app/modules/cms/routes/pages/platform.py +++ b/app/modules/cms/routes/pages/platform.py @@ -164,46 +164,13 @@ async def homepage( logger.info(f"[HOMEPAGE] Rendering CMS homepage with template: {template_path}") return templates.TemplateResponse(template_path, context) - # Fallback: Default orion homepage (no CMS content) - logger.info("[HOMEPAGE] No CMS homepage found, using default orion template") + # Fallback: Default homepage template with placeholder content + logger.info("[HOMEPAGE] No CMS homepage found, using default template with placeholders") context = get_platform_context(request, db) context["tiers"] = _get_tiers_data(db) - # Add-ons (hardcoded for now, will come from DB) - context["addons"] = [ - { - "code": "domain", - "name": "Custom Domain", - "description": "Use your own domain (mydomain.com)", - "price": 15, - "billing_period": "year", - "icon": "globe", - }, - { - "code": "ssl_premium", - "name": "Premium SSL", - "description": "EV certificate for trust badges", - "price": 49, - "billing_period": "year", - "icon": "shield-check", - }, - { - "code": "email", - "name": "Email Package", - "description": "Professional email addresses", - "price": 5, - "billing_period": "month", - "icon": "mail", - "options": [ - {"quantity": 5, "price": 5}, - {"quantity": 10, "price": 9}, - {"quantity": 25, "price": 19}, - ], - }, - ] - return templates.TemplateResponse( - "cms/platform/homepage-orion.html", + "cms/platform/homepage-default.html", context, ) diff --git a/app/modules/cms/routes/pages/storefront.py b/app/modules/cms/routes/pages/storefront.py index a9acb254..92dbd1cf 100644 --- a/app/modules/cms/routes/pages/storefront.py +++ b/app/modules/cms/routes/pages/storefront.py @@ -101,9 +101,17 @@ async def generic_content_page( }, ) + # Resolve placeholders in store default pages ({{store_name}}, etc.) + page_content = page.content + if page.is_store_default and store: + page_content = content_page_service.resolve_placeholders(page.content, store) + + context = get_storefront_context(request, db=db, page=page) + context["page_content"] = page_content + return templates.TemplateResponse( "cms/storefront/content-page.html", - get_storefront_context(request, db=db, page=page), + context, ) diff --git a/app/modules/cms/schemas/__init__.py b/app/modules/cms/schemas/__init__.py index de04a9ec..3cb28100 100644 --- a/app/modules/cms/schemas/__init__.py +++ b/app/modules/cms/schemas/__init__.py @@ -31,6 +31,8 @@ from app.modules.cms.schemas.homepage_sections import ( HomepageSections, HomepageSectionsResponse, PricingSection, + ProductCard, + ProductsSection, # API schemas SectionUpdateRequest, # Translatable text @@ -92,6 +94,8 @@ __all__ = [ "HeroSection", "FeatureCard", "FeaturesSection", + "ProductCard", + "ProductsSection", "PricingSection", "CTASection", "HomepageSections", diff --git a/app/modules/cms/schemas/homepage_sections.py b/app/modules/cms/schemas/homepage_sections.py index 4f9b9e14..ff81f0c6 100644 --- a/app/modules/cms/schemas/homepage_sections.py +++ b/app/modules/cms/schemas/homepage_sections.py @@ -77,6 +77,25 @@ class FeatureCard(BaseModel): description: TranslatableText = Field(default_factory=TranslatableText) +class ProductCard(BaseModel): + """Single product/offering card in products section.""" + + icon: str = "" + title: TranslatableText = Field(default_factory=TranslatableText) + description: TranslatableText = Field(default_factory=TranslatableText) + url: str = "" + badge: TranslatableText | None = None + + +class ProductsSection(BaseModel): + """Product/offering showcase section (e.g. wizard.lu multi-product landing).""" + + enabled: bool = True + title: TranslatableText = Field(default_factory=TranslatableText) + subtitle: TranslatableText | None = None + products: list[ProductCard] = Field(default_factory=list) + + class FeaturesSection(BaseModel): """Features section configuration.""" @@ -114,6 +133,7 @@ class HomepageSections(BaseModel): """Complete homepage sections structure.""" hero: HeroSection | None = None + products: ProductsSection | None = None features: FeaturesSection | None = None pricing: PricingSection | None = None cta: CTASection | None = None @@ -139,6 +159,10 @@ class HomepageSections(BaseModel): subtitle=make_translatable(languages), buttons=[], ), + products=ProductsSection( + title=make_translatable(languages), + products=[], + ), features=FeaturesSection( title=make_translatable(languages), features=[], @@ -162,7 +186,7 @@ class HomepageSections(BaseModel): class SectionUpdateRequest(BaseModel): """Request to update a single section.""" - section_name: str = Field(..., description="hero, features, pricing, or cta") + section_name: str = Field(..., description="hero, products, features, pricing, or cta") section_data: dict = Field(..., description="Section configuration") diff --git a/app/modules/cms/services/content_page_service.py b/app/modules/cms/services/content_page_service.py index 8631ecd2..e42274f7 100644 --- a/app/modules/cms/services/content_page_service.py +++ b/app/modules/cms/services/content_page_service.py @@ -142,8 +142,8 @@ class ContentPageService: db.query(ContentPage) .filter( and_( - ContentPage.store_id is None, - ContentPage.is_platform_page == False, + ContentPage.store_id.is_(None), + ContentPage.is_platform_page.is_(False), *base_filters, ) ) @@ -182,12 +182,12 @@ class ContentPageService: filters = [ ContentPage.platform_id == platform_id, ContentPage.slug == slug, - ContentPage.store_id is None, - ContentPage.is_platform_page == True, + ContentPage.store_id.is_(None), + ContentPage.is_platform_page.is_(True), ] if not include_unpublished: - filters.append(ContentPage.is_published == True) + filters.append(ContentPage.is_published.is_(True)) page = db.query(ContentPage).filter(and_(*filters)).first() @@ -255,8 +255,8 @@ class ContentPageService: db.query(ContentPage) .filter( and_( - ContentPage.store_id is None, - ContentPage.is_platform_page == False, + ContentPage.store_id.is_(None), + ContentPage.is_platform_page.is_(False), *base_filters, ) ) @@ -298,12 +298,12 @@ class ContentPageService: """ filters = [ ContentPage.platform_id == platform_id, - ContentPage.store_id is None, - ContentPage.is_platform_page == True, + ContentPage.store_id.is_(None), + ContentPage.is_platform_page.is_(True), ] if not include_unpublished: - filters.append(ContentPage.is_published == True) + filters.append(ContentPage.is_published.is_(True)) if footer_only: filters.append(ContentPage.show_in_footer == True) @@ -377,12 +377,12 @@ class ContentPageService: """ filters = [ ContentPage.platform_id == platform_id, - ContentPage.store_id is None, - ContentPage.is_platform_page == False, + ContentPage.store_id.is_(None), + ContentPage.is_platform_page.is_(False), ] if not include_unpublished: - filters.append(ContentPage.is_published == True) + filters.append(ContentPage.is_published.is_(True)) return ( db.query(ContentPage) @@ -845,13 +845,13 @@ class ContentPageService: filters.append(ContentPage.is_published == True) if page_tier == "platform": - filters.append(ContentPage.is_platform_page == True) - filters.append(ContentPage.store_id is None) + filters.append(ContentPage.is_platform_page.is_(True)) + filters.append(ContentPage.store_id.is_(None)) elif page_tier == "store_default": - filters.append(ContentPage.is_platform_page == False) - filters.append(ContentPage.store_id is None) + filters.append(ContentPage.is_platform_page.is_(False)) + filters.append(ContentPage.store_id.is_(None)) elif page_tier == "store_override": - filters.append(ContentPage.store_id is not None) + filters.append(ContentPage.store_id.isnot(None)) return ( db.query(ContentPage) @@ -958,6 +958,34 @@ class ContentPageService: if not success: raise ContentPageNotFoundException(identifier=page_id) + # ========================================================================= + # Placeholder Resolution (for store default pages) + # ========================================================================= + + @staticmethod + def resolve_placeholders(content: str, store) -> str: + """ + Replace {{store_name}}, {{store_email}}, {{store_phone}} placeholders + in store default page content with actual store values. + + Args: + content: HTML content with placeholders + store: Store object with name, contact_email, phone attributes + + Returns: + Content with placeholders replaced + """ + if not content or not store: + return content or "" + replacements = { + "{{store_name}}": store.name or "Our Store", + "{{store_email}}": getattr(store, "contact_email", "") or "", + "{{store_phone}}": getattr(store, "phone", "") or "", + } + for placeholder, value in replacements.items(): + content = content.replace(placeholder, value) + return content + # ========================================================================= # Homepage Sections Management # ========================================================================= @@ -1032,10 +1060,12 @@ class ContentPageService: FeaturesSection, HeroSection, PricingSection, + ProductsSection, ) SECTION_SCHEMAS = { "hero": HeroSection, + "products": ProductsSection, "features": FeaturesSection, "pricing": PricingSection, "cta": CTASection, diff --git a/app/modules/cms/templates/cms/admin/content-page-edit.html b/app/modules/cms/templates/cms/admin/content-page-edit.html index 35580152..dabe531c 100644 --- a/app/modules/cms/templates/cms/admin/content-page-edit.html +++ b/app/modules/cms/templates/cms/admin/content-page-edit.html @@ -187,6 +187,35 @@
+ + {# Available Placeholders (for store default pages) #} + {% set placeholders = [ + ('store_name', "The store's display name"), + ('store_email', "The store's contact email"), + ('store_phone', "The store's phone number"), + ] %} ++ Use these placeholders in store default pages. They will be automatically replaced with the store's actual information when displayed. +
+
+ {% raw %}{{{% endraw %}{{ name }}{% raw %}}}{% endraw %}
+
+ {% endfor %}
+ + Click a placeholder to copy it to your clipboard. +
+- {{ _("cms.platform.hero.subtitle") }} -
- - {# CTA Buttons #} - -- {{ _("cms.platform.pricing.subtitle", trial_days=trial_days) }} -
- - {# Billing Toggle #} -- {{ _("cms.platform.addons.subtitle") }} -
-{{ addon.description }}
- - {# Price #} -- {{ _("cms.platform.find_shop.subtitle") }} -
-- {{ _("cms.platform.find_shop.no_account") }} {{ _("cms.platform.find_shop.signup_letzshop") }}{{ _("cms.platform.find_shop.then_connect") }} -
-- {{ _("cms.platform.cta.subtitle", trial_days=trial_days) }} -
- - {{ _("cms.platform.cta.button") }} - - -+ {{ subtitle }} +
+ {% endif %} + {% endif %} ++ {{ product_desc }} +
+ {% endif %} + + {# CTA Link #} + {% if product.url %} + + {% set link_text = product_title or 'Learn More' %} + Learn More + + + {% endif %} +- Connect stores with customers worldwide. Build your online store and reach millions of shoppers. -
-- Our platform empowers entrepreneurs to launch their own branded e-commerce stores - with minimal effort and maximum impact. -
- """, - "meta_description": "Leading multi-store marketplace platform. Connect with thousands of stores and discover millions of products.", - "meta_keywords": "marketplace, multi-store, e-commerce, online shopping, platform", - "show_in_footer": False, - "show_in_header": False, - "is_platform_page": True, - "template": "modern", - "display_order": 0, + +def t(fr: str, en: str, de: str) -> dict: + """Build a TranslatableText-compatible dict.""" + return {"translations": {"fr": fr, "en": en, "de": de}} + + +# ============================================================================ +# 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/", + }, + { + "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/", + }, + { + "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, + }, + "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, + }, + "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, } + # ============================================================================ -# DEFAULT STORE CONTENT PAGES (is_platform_page=False) +# PLATFORM MARKETING PAGES (is_platform_page=True) # ============================================================================ -DEFAULT_PAGES = [ + +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", + "content": """Wizard is the digital toolkit for Luxembourg businesses. We provide integrated solutions for order management, customer loyalty, and online presence.
+To empower Luxembourg businesses with modern, easy-to-use digital tools that help them grow and thrive in the digital age.
+We understand the unique needs of Luxembourg commerce — from multilingual support (FR/DE/EN) to EU VAT compliance.
+We'd love to hear from you. Get in touch with our team.
+Interested in our solutions for your business?
+Already a customer? Our support team is here to help.
+Luxembourg
+Wizard is a suite of digital tools for Luxembourg businesses, including order management (OMS), customer loyalty programs, and website building.
+Wizard is designed for Luxembourg businesses of all sizes — from individual Letzshop sellers to multi-store retailers.
+Yes! All our solutions offer a free trial period. No credit card required to start.
+Yes, you can upgrade or downgrade your plan at any time.
+Our platform supports French, German, and English — the three official languages of Luxembourg.
+Yes. We use industry-standard encryption and follow GDPR regulations for data protection.
+OMS (omsflow.lu) is a lightweight order management system built specifically for Letzshop sellers in Luxembourg.
+Letzshop is a great marketplace, but it doesn't give sellers the back-office tools they need. OMS fills that gap with:
+From multilingual support to Luxembourg VAT compliance, OMS is designed for the local market.
+Need help with your order management? We're here for you.
+Interested in OMS for your Letzshop store?
+While OMS works best with Letzshop integration, you can also use it as a standalone order management tool.
+Enter your Letzshop API credentials and orders sync automatically. Setup takes about 2 minutes.
+Yes, EU VAT invoicing is available on Professional and Enterprise plans. Luxembourg VAT is available on all plans.
+Yes, you can export customers, orders, and invoices at any time.
+Yes! Start with a free trial, no credit card required.
+Loyalty (rewardflow.lu) helps Luxembourg businesses create engaging customer loyalty programs.
+Multilingual support (FR/DE/EN) and local business features out of the box.
+Need help with your loyalty program? We're here for you.
+Interested in a loyalty program for your business?
+You define the rules — for example, 1 point per euro spent. Points can be redeemed for rewards from your catalog.
+Yes! You can create custom tiers with different names, thresholds, and benefits.
+Customers can check their balance through a personalized loyalty page or at your point of sale.
+Yes! Start with a free trial, no credit card required.
+Last Updated: February 2026
+We collect information you provide directly:
+We use your information to provide and improve our services, process payments, communicate with you, and comply with legal obligations.
+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.
+For privacy-related questions, contact privacy@wizard.lu
+Last Updated: February 2026
+By accessing and using this platform, you accept and agree to be bound by these Terms of Service.
+We provide digital business tools including order management, loyalty programs, and website building services.
+You must provide accurate information and maintain the security of your account.
+Subscription fees are billed monthly or annually. You may cancel at any time.
+Your use of our services is also governed by our Privacy Policy.
+These terms are governed by the laws of Luxembourg.
+For questions about these terms, contact legal@wizard.lu
+We are a multi-store e-commerce platform connecting quality sellers with customers worldwide.
- -To empower independent businesses and artisans by providing them with the tools and platform they need to reach customers globally.
- -Welcome to {{store_name}}. We are committed to providing you with quality products and excellent service.
+{{store_name}} was founded with a simple mission: to deliver exceptional value to our customers.
+Have questions? We'd love to hear from you.
We're here to help! Reach out to us through any of the following channels:
- -For questions about orders, products, or general inquiries:
+ "content": """We're here to help! Reach out to us.
+Interested in becoming a store or partnering with us?
-
- 123 Commerce Street
- Suite 456
- City, State 12345
- United States
-
We typically respond to all inquiries within 24 hours during business days.
-We typically respond within 24 hours during business days.
+Once your order ships, you'll receive a tracking number via email. You can also view your order status in your account dashboard.
- -Shipping times vary by store and destination. Most orders arrive within 3-7 business days. See our Shipping Policy for details.
- -Yes! We ship to most countries worldwide. International shipping times and costs vary by destination.
- -Most items can be returned within 30 days of delivery. See our Return Policy for complete details.
- -Contact the store directly through your order page or reach out to our support team for assistance.
- -We accept all major credit cards, PayPal, and other secure payment methods.
- -Yes! We use industry-standard encryption and never store your full payment details.
- -No, you can checkout as a guest. However, creating an account lets you track orders and save your preferences.
- -Click "Forgot Password" on the login page and follow the instructions sent to your email.
-You can reach us at {{store_email}} or call {{store_phone}}.
+We are available during regular business hours, Monday through Friday.
+Please contact us for information about our location and visiting hours.
+{{store_name}} is committed to protecting your personal information.
+We collect information necessary to process your orders and provide customer service.
+Under GDPR, you have the right to access, correct, or delete your personal data. Contact us at {{store_email}}.
+By using the services provided by {{store_name}}, you agree to these terms.
+All orders are subject to availability and confirmation.
+For questions about these terms, contact us at {{store_email}}.
+We offer multiple shipping options to meet your needs:
+{{store_name}} offers multiple shipping options:
Shipping costs are calculated based on:
-Free standard shipping on orders over $50!
- -Orders are typically processed within 1-2 business days. You'll receive a confirmation email when your order ships with tracking information.
- -We ship to most countries worldwide. International shipping times vary by destination (typically 7-21 business days).
-Shipping costs are calculated based on weight and destination at checkout.
All orders include tracking. You can monitor your package status through:
-If you experience any delivery issues, please contact us within 7 days of the expected delivery date.
-You will receive a tracking number by email once your order ships.
+Contact us at {{store_email}} for shipping inquiries.
+Most items can be returned within 30 days of delivery for a full refund or exchange.
- -To be eligible for a return, items must:
-The following items cannot be returned:
-{{store_name}} accepts returns within 14 days of delivery, in accordance with Luxembourg consumer protection law.
Once we receive your return:
-Return shipping is free for defective or incorrect items. For other returns, a $5.99 return shipping fee will be deducted from your refund.
- -We offer free exchanges for different sizes or colors of the same item. Contact support to arrange an exchange.
- -If you receive a damaged or defective item, please contact us within 7 days of delivery with photos. We'll send a replacement or full refund immediately.
-Refunds are processed within 14 days of receiving the returned item, back to the original payment method.
+If you receive a damaged item, contact us immediately at {{store_email}} with photos.
+Last Updated: [Date]
- -We collect information you provide directly:
-We automatically collect:
-We use your information to:
-We share your information only when necessary:
-We never sell your personal information to third parties.
- -You have the right to:
-We implement industry-standard security measures including:
-We use cookies to enhance your experience. You can control cookies through your browser settings.
- -Our platform is not intended for children under 13. We do not knowingly collect information from children.
- -We may update this policy periodically. Significant changes will be communicated via email.
- -For privacy-related questions, contact us at privacy@example.com
-Last Updated: [Date]
- -By accessing and using this platform, you accept and agree to be bound by these Terms of Service.
- -You must:
-You agree not to:
-Product information is provided by stores. While we strive for accuracy:
-All content on this platform is protected by copyright, trademark, and other laws. You may not:
-This platform is provided "as is" without warranties of any kind, express or implied.
- -We are not liable for indirect, incidental, or consequential damages arising from your use of the platform.
- -You agree to indemnify and hold us harmless from claims arising from your use of the platform or violation of these terms.
- -Disputes will be resolved through binding arbitration in accordance with the rules of the American Arbitration Association.
- -These terms are governed by the laws of [State/Country], without regard to conflict of law principles.
- -We may modify these terms at any time. Continued use constitutes acceptance of modified terms.
- -For questions about these terms, contact legal@example.com
-