feat(storefront): section-based homepages, header action partials, fixes
Phase 1 — Section-based store homepages:
- Store defaults use template="full" with per-platform sections JSON
- OMS: shop hero + features + CTA; Loyalty: rewards hero + features + CTA
- Hosting: services hero + features + CTA
- Deep placeholder resolution for {{store_name}} inside sections JSON
- landing-full.html uses resolved page_sections from context
Phase 2 — Module-contributed header actions:
- header_template field on MenuItemDefinition + DiscoveredMenuItem
- Catalog provides header-search.html partial
- Cart provides header-cart.html partial with badge
- Base template iterates storefront_nav.actions with {% include %}
- Generic icon fallback for actions without a template
Fixes:
- Store theme API: get_store_by_code → get_store_by_code_or_subdomain
Docs:
- CMS redesign proposal: menu restructure, page types, translations UI
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2099,47 +2099,319 @@ SHARED_PLATFORM_PAGES = [
|
||||
|
||||
STORE_DEFAULT_HOME = {
|
||||
"slug": "home",
|
||||
"title": "Welcome to {{store_name}}",
|
||||
"title": "{{store_name}}",
|
||||
"title_translations": tt(
|
||||
"Welcome to {{store_name}}",
|
||||
"Bienvenue chez {{store_name}}",
|
||||
"Willkommen bei {{store_name}}",
|
||||
"Wëllkomm bei {{store_name}}",
|
||||
"{{store_name}}",
|
||||
"{{store_name}}",
|
||||
"{{store_name}}",
|
||||
"{{store_name}}",
|
||||
),
|
||||
"content": """<div class="prose-content">
|
||||
<h2>Welcome</h2>
|
||||
<p>{{store_name}} is here to serve you. Browse our offerings and discover what we have for you.</p>
|
||||
</div>""",
|
||||
"content_translations": tt(
|
||||
# English
|
||||
"""<div class="prose-content">
|
||||
<h2>Welcome</h2>
|
||||
<p>{{store_name}} is here to serve you. Browse our offerings and discover what we have for you.</p>
|
||||
</div>""",
|
||||
# French
|
||||
"""<div class="prose-content">
|
||||
<h2>Bienvenue</h2>
|
||||
<p>{{store_name}} est là pour vous servir. Découvrez nos offres et ce que nous avons pour vous.</p>
|
||||
</div>""",
|
||||
# German
|
||||
"""<div class="prose-content">
|
||||
<h2>Willkommen</h2>
|
||||
<p>{{store_name}} ist für Sie da. Entdecken Sie unser Angebot.</p>
|
||||
</div>""",
|
||||
# Luxembourgish
|
||||
"""<div class="prose-content">
|
||||
<h2>Wëllkomm</h2>
|
||||
<p>{{store_name}} ass fir Iech do. Entdeckt eist Angebot.</p>
|
||||
</div>""",
|
||||
),
|
||||
"template": "default",
|
||||
"meta_description": "Welcome to {{store_name}}",
|
||||
"content": "",
|
||||
"template": "full",
|
||||
"meta_description": "{{store_name}}",
|
||||
"show_in_header": False,
|
||||
"show_in_footer": False,
|
||||
"display_order": 0,
|
||||
}
|
||||
|
||||
|
||||
def _store_homepage_sections_oms() -> dict:
|
||||
"""Store homepage sections for OMS platform stores."""
|
||||
return {
|
||||
"hero": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Bienvenue chez {{store_name}}",
|
||||
"Welcome to {{store_name}}",
|
||||
"Willkommen bei {{store_name}}",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Découvrez notre sélection de produits et profitez d'une expérience d'achat exceptionnelle.",
|
||||
"Discover our product selection and enjoy an exceptional shopping experience.",
|
||||
"Entdecken Sie unsere Produktauswahl und genießen Sie ein außergewöhnliches Einkaufserlebnis.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("Voir nos produits", "Browse Products", "Produkte ansehen"),
|
||||
"url": "products",
|
||||
"style": "primary",
|
||||
},
|
||||
{
|
||||
"text": t("À propos", "About Us", "Über uns"),
|
||||
"url": "about",
|
||||
"style": "secondary",
|
||||
},
|
||||
],
|
||||
},
|
||||
"features": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Pourquoi nous choisir",
|
||||
"Why Choose Us",
|
||||
"Warum uns wählen",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Ce qui nous distingue",
|
||||
"What sets us apart",
|
||||
"Was uns auszeichnet",
|
||||
),
|
||||
"layout": "grid",
|
||||
"features": [
|
||||
{
|
||||
"icon": "check",
|
||||
"title": t("Qualité premium", "Premium Quality", "Premium-Qualität"),
|
||||
"description": t(
|
||||
"Des produits soigneusement sélectionnés pour vous.",
|
||||
"Carefully selected products just for you.",
|
||||
"Sorgfältig ausgewählte Produkte für Sie.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "truck",
|
||||
"title": t("Livraison rapide", "Fast Shipping", "Schnelle Lieferung"),
|
||||
"description": t(
|
||||
"Livraison rapide directement chez vous.",
|
||||
"Quick delivery right to your door.",
|
||||
"Schnelle Lieferung direkt zu Ihnen.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "shield-check",
|
||||
"title": t("Paiement sécurisé", "Secure Payment", "Sichere Zahlung"),
|
||||
"description": t(
|
||||
"Vos transactions sont protégées à 100%.",
|
||||
"Your transactions are 100% protected.",
|
||||
"Ihre Transaktionen sind 100% geschützt.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "chat-bubble-left",
|
||||
"title": t("Support client", "Customer Support", "Kundensupport"),
|
||||
"description": t(
|
||||
"Une équipe à votre écoute pour vous accompagner.",
|
||||
"A dedicated team ready to assist you.",
|
||||
"Ein engagiertes Team, das Ihnen zur Seite steht.",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
"cta": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Prêt à découvrir nos produits ?",
|
||||
"Ready to Explore Our Products?",
|
||||
"Bereit, unsere Produkte zu entdecken?",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Parcourez notre catalogue et trouvez ce qui vous convient.",
|
||||
"Browse our catalog and find what suits you.",
|
||||
"Durchstöbern Sie unseren Katalog und finden Sie, was zu Ihnen passt.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("Voir les produits", "View Products", "Produkte ansehen"),
|
||||
"url": "products",
|
||||
"style": "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _store_homepage_sections_loyalty() -> dict:
|
||||
"""Store homepage sections for Loyalty platform stores."""
|
||||
return {
|
||||
"hero": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Bienvenue chez {{store_name}}",
|
||||
"Welcome to {{store_name}}",
|
||||
"Willkommen bei {{store_name}}",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Rejoignez notre programme de fidélité et profitez de récompenses exclusives à chaque visite.",
|
||||
"Join our loyalty program and enjoy exclusive rewards with every visit.",
|
||||
"Treten Sie unserem Treueprogramm bei und genießen Sie exklusive Prämien bei jedem Besuch.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("Rejoindre le programme", "Join Rewards", "Programm beitreten"),
|
||||
"url": "account/loyalty",
|
||||
"style": "primary",
|
||||
},
|
||||
{
|
||||
"text": t("En savoir plus", "Learn More", "Mehr erfahren"),
|
||||
"url": "about",
|
||||
"style": "secondary",
|
||||
},
|
||||
],
|
||||
},
|
||||
"features": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Votre fidélité récompensée",
|
||||
"Your Loyalty Rewarded",
|
||||
"Ihre Treue wird belohnt",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Découvrez les avantages de notre programme",
|
||||
"Discover the benefits of our program",
|
||||
"Entdecken Sie die Vorteile unseres Programms",
|
||||
),
|
||||
"layout": "grid",
|
||||
"features": [
|
||||
{
|
||||
"icon": "star",
|
||||
"title": t("Gagnez des points", "Earn Points", "Punkte sammeln"),
|
||||
"description": t(
|
||||
"Cumulez des points à chaque achat et échangez-les contre des récompenses.",
|
||||
"Accumulate points with every purchase and redeem them for rewards.",
|
||||
"Sammeln Sie bei jedem Einkauf Punkte und lösen Sie sie gegen Prämien ein.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "gift",
|
||||
"title": t("Récompenses exclusives", "Exclusive Rewards", "Exklusive Prämien"),
|
||||
"description": t(
|
||||
"Accédez à des offres et récompenses réservées aux membres.",
|
||||
"Access offers and rewards reserved for members.",
|
||||
"Zugang zu Angeboten und Prämien nur für Mitglieder.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "heart",
|
||||
"title": t("Avantages membres", "Member Benefits", "Mitgliedervorteile"),
|
||||
"description": t(
|
||||
"Profitez d'avantages exclusifs en tant que membre fidèle.",
|
||||
"Enjoy exclusive benefits as a loyal member.",
|
||||
"Genießen Sie exklusive Vorteile als treues Mitglied.",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
"cta": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Rejoignez-nous aujourd'hui",
|
||||
"Join Us Today",
|
||||
"Treten Sie uns noch heute bei",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Inscrivez-vous à notre programme de fidélité et commencez à gagner des récompenses.",
|
||||
"Sign up for our loyalty program and start earning rewards.",
|
||||
"Melden Sie sich für unser Treueprogramm an und beginnen Sie, Prämien zu verdienen.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("S'inscrire", "Sign Up", "Anmelden"),
|
||||
"url": "account/loyalty",
|
||||
"style": "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _store_homepage_sections_hosting() -> dict:
|
||||
"""Store homepage sections for Hosting platform stores (client sites)."""
|
||||
return {
|
||||
"hero": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Bienvenue chez {{store_name}}",
|
||||
"Welcome to {{store_name}}",
|
||||
"Willkommen bei {{store_name}}",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Votre partenaire de confiance pour des solutions numériques sur mesure.",
|
||||
"Your trusted partner for tailored digital solutions.",
|
||||
"Ihr vertrauenswürdiger Partner für maßgeschneiderte digitale Lösungen.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("Nous contacter", "Contact Us", "Kontaktieren Sie uns"),
|
||||
"url": "contact",
|
||||
"style": "primary",
|
||||
},
|
||||
{
|
||||
"text": t("À propos", "About Us", "Über uns"),
|
||||
"url": "about",
|
||||
"style": "secondary",
|
||||
},
|
||||
],
|
||||
},
|
||||
"features": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Nos services",
|
||||
"Our Services",
|
||||
"Unsere Dienstleistungen",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Ce que nous pouvons faire pour vous",
|
||||
"What we can do for you",
|
||||
"Was wir für Sie tun können",
|
||||
),
|
||||
"layout": "grid",
|
||||
"features": [
|
||||
{
|
||||
"icon": "globe-alt",
|
||||
"title": t("Site web", "Website", "Webseite"),
|
||||
"description": t(
|
||||
"Un site web professionnel qui reflète votre activité.",
|
||||
"A professional website that reflects your business.",
|
||||
"Eine professionelle Website, die Ihr Unternehmen widerspiegelt.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "shield-check",
|
||||
"title": t("Hébergement sécurisé", "Secure Hosting", "Sicheres Hosting"),
|
||||
"description": t(
|
||||
"Hébergement fiable avec certificat SSL inclus.",
|
||||
"Reliable hosting with SSL certificate included.",
|
||||
"Zuverlässiges Hosting mit SSL-Zertifikat inklusive.",
|
||||
),
|
||||
},
|
||||
{
|
||||
"icon": "mail",
|
||||
"title": t("Email professionnel", "Professional Email", "Professionelle E-Mail"),
|
||||
"description": t(
|
||||
"Adresses email personnalisées pour votre entreprise.",
|
||||
"Custom email addresses for your business.",
|
||||
"Individuelle E-Mail-Adressen für Ihr Unternehmen.",
|
||||
),
|
||||
},
|
||||
],
|
||||
},
|
||||
"cta": {
|
||||
"enabled": True,
|
||||
"title": t(
|
||||
"Besoin d'aide ?",
|
||||
"Need Help?",
|
||||
"Brauchen Sie Hilfe?",
|
||||
),
|
||||
"subtitle": t(
|
||||
"Contactez-nous pour discuter de votre projet.",
|
||||
"Contact us to discuss your project.",
|
||||
"Kontaktieren Sie uns, um Ihr Projekt zu besprechen.",
|
||||
),
|
||||
"background_type": "gradient",
|
||||
"buttons": [
|
||||
{
|
||||
"text": t("Nous contacter", "Contact Us", "Kontaktieren Sie uns"),
|
||||
"url": "contact",
|
||||
"style": "primary",
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
STORE_DEFAULTS_COMMON = [
|
||||
{
|
||||
"slug": "about",
|
||||
@@ -2796,8 +3068,18 @@ def create_default_pages(db: Session) -> None:
|
||||
# Only for platforms that host stores (not wizard.lu main)
|
||||
# ------------------------------------------------------------------
|
||||
if platform.code != "main":
|
||||
# Store homepage (slug="home")
|
||||
if _create_page(db, platform.id, STORE_DEFAULT_HOME, is_platform_page=False):
|
||||
# Store homepage (slug="home") with platform-specific sections
|
||||
store_sections_map = {
|
||||
"oms": _store_homepage_sections_oms,
|
||||
"loyalty": _store_homepage_sections_loyalty,
|
||||
"hosting": _store_homepage_sections_hosting,
|
||||
}
|
||||
store_sections_fn = store_sections_map.get(platform.code)
|
||||
store_sections = store_sections_fn() if store_sections_fn else None
|
||||
if _create_page(
|
||||
db, platform.id, STORE_DEFAULT_HOME,
|
||||
is_platform_page=False, sections=store_sections,
|
||||
):
|
||||
created_count += 1
|
||||
else:
|
||||
skipped_count += 1
|
||||
|
||||
Reference in New Issue
Block a user