Files
orion/app/modules/cms/schemas/homepage_sections.py
Samir Boulahtit 011a4df2d4
Some checks failed
CI / architecture (push) Failing after 8s
CI / ruff (push) Failing after 7s
CI / pytest (push) Failing after 0s
CI / dependency-scanning (push) Successful in 26s
CI / audit (push) Successful in 8s
CI / docs (push) Has been skipped
fix(lint): fix dict comprehension and import sorting
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 23:29:10 +01:00

175 lines
5.5 KiB
Python

"""
Homepage Section Schemas with Dynamic Multi-Language Support.
Language codes are NOT hardcoded - they come from platform.supported_languages.
The TranslatableText class stores translations as a dict where keys are language codes.
Example JSON structure:
{
"hero": {
"enabled": true,
"title": {"translations": {"fr": "Bienvenue", "en": "Welcome"}},
"subtitle": {"translations": {...}},
"buttons": [...]
},
"features": {...},
"pricing": {...},
"cta": {...}
}
"""
from pydantic import BaseModel, Field
class TranslatableText(BaseModel):
"""
Text field with translations stored as language-keyed dict.
Languages come from platform.supported_languages (not hardcoded).
Use .get(lang, default_lang) to retrieve translation with fallback.
"""
translations: dict[str, str] = Field(
default_factory=dict, description="Language code -> translated text mapping"
)
def get(self, lang: str, default_lang: str = "fr") -> str:
"""Get translation with fallback to default language."""
return self.translations.get(lang) or self.translations.get(default_lang) or ""
def set(self, lang: str, text: str) -> None:
"""Set translation for a language."""
self.translations[lang] = text
def has_translation(self, lang: str) -> bool:
"""Check if translation exists for language."""
return bool(self.translations.get(lang))
class HeroButton(BaseModel):
"""Button in hero or CTA section."""
text: TranslatableText = Field(default_factory=TranslatableText)
url: str = ""
style: str = Field(default="primary", description="primary, secondary, outline")
class HeroSection(BaseModel):
"""Hero section configuration."""
enabled: bool = True
badge_text: TranslatableText | None = None
title: TranslatableText = Field(default_factory=TranslatableText)
subtitle: TranslatableText = Field(default_factory=TranslatableText)
background_type: str = Field(
default="gradient", description="gradient, image, solid"
)
background_image: str | None = None
buttons: list[HeroButton] = Field(default_factory=list)
class FeatureCard(BaseModel):
"""Single feature in features section."""
icon: str = ""
title: TranslatableText = Field(default_factory=TranslatableText)
description: TranslatableText = Field(default_factory=TranslatableText)
class FeaturesSection(BaseModel):
"""Features section configuration."""
enabled: bool = True
title: TranslatableText = Field(default_factory=TranslatableText)
subtitle: TranslatableText | None = None
features: list[FeatureCard] = Field(default_factory=list)
layout: str = Field(default="grid", description="grid, list, cards")
class PricingSection(BaseModel):
"""Pricing section configuration."""
enabled: bool = True
title: TranslatableText = Field(default_factory=TranslatableText)
subtitle: TranslatableText | None = None
use_subscription_tiers: bool = Field(
default=True, description="Pull pricing from subscription_tiers table dynamically"
)
class CTASection(BaseModel):
"""Call-to-action section configuration."""
enabled: bool = True
title: TranslatableText = Field(default_factory=TranslatableText)
subtitle: TranslatableText | None = None
buttons: list[HeroButton] = Field(default_factory=list)
background_type: str = Field(
default="gradient", description="gradient, image, solid"
)
class HomepageSections(BaseModel):
"""Complete homepage sections structure."""
hero: HeroSection | None = None
features: FeaturesSection | None = None
pricing: PricingSection | None = None
cta: CTASection | None = None
@classmethod
def get_empty_structure(cls, languages: list[str]) -> "HomepageSections":
"""
Create empty section structure with language placeholders.
Args:
languages: List of language codes from platform.supported_languages
Returns:
HomepageSections with empty translations for all languages
"""
def make_translatable(langs: list[str]) -> TranslatableText:
return TranslatableText(translations=dict.fromkeys(langs, ""))
return cls(
hero=HeroSection(
title=make_translatable(languages),
subtitle=make_translatable(languages),
buttons=[],
),
features=FeaturesSection(
title=make_translatable(languages),
features=[],
),
pricing=PricingSection(
title=make_translatable(languages),
use_subscription_tiers=True,
),
cta=CTASection(
title=make_translatable(languages),
buttons=[],
),
)
# =============================================================================
# API Request/Response Schemas
# =============================================================================
class SectionUpdateRequest(BaseModel):
"""Request to update a single section."""
section_name: str = Field(..., description="hero, features, pricing, or cta")
section_data: dict = Field(..., description="Section configuration")
class HomepageSectionsResponse(BaseModel):
"""Response containing all homepage sections with platform language info."""
sections: HomepageSections | None = None
supported_languages: list[str] = Field(default_factory=lambda: ["fr", "de", "en"])
default_language: str = "fr"