Add structured JSON sections to ContentPage for multi-language homepage editing:
Database:
- Add `sections` JSON column to content_pages table
- Migration z8i9j0k1l2m3 adds the column
Schema:
- New models/schema/homepage_sections.py with Pydantic schemas
- TranslatableText for language-keyed translations
- HeroSection, FeaturesSection, PricingSection, CTASection
Templates:
- New section partials in app/templates/platform/sections/
- Updated homepage-default.html to render sections dynamically
- Fallback to placeholder content when sections not configured
Service:
- update_homepage_sections() - validate and save all sections
- update_single_section() - update individual section
- get_default_sections() - empty structure for new homepages
API:
- GET /{page_id}/sections - get sections with platform languages
- PUT /{page_id}/sections - update all sections
- PUT /{page_id}/sections/{section_name} - update single section
Admin UI:
- Section editor appears when editing homepage (slug='home')
- Language tabs from platform.supported_languages
- Accordion sections for Hero, Features, Pricing, CTA
- Button/feature card repeaters with add/remove
Also fixes broken line 181 in z4e5f6a7b8c9 migration.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
72 lines
3.2 KiB
HTML
72 lines
3.2 KiB
HTML
{# app/templates/platform/sections/_hero.html #}
|
|
{# Hero section partial with multi-language support #}
|
|
{#
|
|
Parameters:
|
|
- hero: HeroSection object (or dict)
|
|
- lang: Current language code (from request.state.language)
|
|
- default_lang: Fallback language (from platform.default_language)
|
|
#}
|
|
|
|
{% macro render_hero(hero, lang, default_lang) %}
|
|
{% if hero and hero.enabled %}
|
|
<section class="gradient-primary text-white py-20 relative overflow-hidden">
|
|
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div class="text-center">
|
|
{# Badge #}
|
|
{% if hero.badge_text and hero.badge_text.translations %}
|
|
{% set badge = hero.badge_text.translations.get(lang) or hero.badge_text.translations.get(default_lang) %}
|
|
{% if badge %}
|
|
<div class="inline-flex items-center px-4 py-2 bg-white/20 backdrop-blur-sm rounded-full text-white text-sm font-medium mb-6">
|
|
{{ badge }}
|
|
</div>
|
|
{% endif %}
|
|
{% endif %}
|
|
|
|
{# Title #}
|
|
{% set title = hero.title.translations.get(lang) or hero.title.translations.get(default_lang) or '' %}
|
|
{% if title %}
|
|
<h1 class="text-4xl md:text-5xl lg:text-6xl font-extrabold leading-tight mb-6">
|
|
{{ title }}
|
|
</h1>
|
|
{% endif %}
|
|
|
|
{# Subtitle #}
|
|
{% set subtitle = hero.subtitle.translations.get(lang) or hero.subtitle.translations.get(default_lang) or '' %}
|
|
{% if subtitle %}
|
|
<p class="text-xl md:text-2xl mb-10 opacity-90 max-w-3xl mx-auto">
|
|
{{ subtitle }}
|
|
</p>
|
|
{% endif %}
|
|
|
|
{# Buttons #}
|
|
{% if hero.buttons %}
|
|
<div class="flex flex-col sm:flex-row gap-4 justify-center items-center">
|
|
{% for button in hero.buttons %}
|
|
{% set btn_text = button.text.translations.get(lang) or button.text.translations.get(default_lang) or '' %}
|
|
{% if btn_text and button.url %}
|
|
<a href="{{ button.url }}"
|
|
class="{% if button.style == 'primary' %}bg-white text-gray-900 hover:bg-gray-100{% elif button.style == 'secondary' %}bg-white/20 text-white hover:bg-white/30{% else %}border-2 border-white text-white hover:bg-white/10{% endif %} px-8 py-4 rounded-xl font-semibold transition inline-flex items-center space-x-2">
|
|
<span>{{ btn_text }}</span>
|
|
{% if button.style == 'primary' %}
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 7l5 5m0 0l-5 5m5-5H6"></path>
|
|
</svg>
|
|
{% endif %}
|
|
</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
{# Background decorations #}
|
|
<div class="absolute top-0 right-0 w-1/3 h-full opacity-10">
|
|
<svg viewBox="0 0 200 200" class="w-full h-full">
|
|
<circle cx="100" cy="100" r="80" fill="white"/>
|
|
</svg>
|
|
</div>
|
|
</section>
|
|
{% endif %}
|
|
{% endmacro %}
|