feat(cms): CMS-driven homepages, products section, placeholder resolution
Some checks failed
Some checks failed
- Add ProductCard/ProductsSection schema and _products.html section macro
- Rewrite seed script with 3-platform homepage sections (wizard, OMS, loyalty),
platform marketing pages, and store defaults with {{store_name}} placeholders
- Add resolve_placeholders() to ContentPageService for store default pages
- Fix SQLAlchemy filter bugs: replace Python `is None` with `.is_(None)` across
all ContentPageService query methods (was silently breaking all platform page lookups)
- Remove hardcoded orion fallback and delete homepage-orion.html
- Add placeholder hint box with click-to-copy in admin content page editor
- Export ProductCard/ProductsSection from cms schemas __init__
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -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",
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user