feat(cms): CMS-driven homepages, products section, placeholder resolution
Some checks failed
CI / ruff (push) Successful in 11s
CI / pytest (push) Failing after 51m41s
CI / validate (push) Successful in 26s
CI / dependency-scanning (push) Successful in 32s
CI / docs (push) Has been skipped
CI / deploy (push) Has been skipped

- 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:
2026-03-01 12:12:20 +01:00
parent ef9ea29643
commit adbecd360b
11 changed files with 1132 additions and 916 deletions

View File

@@ -187,6 +187,35 @@
</p>
</div>
</div>
{# 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"),
] %}
<div x-show="!form.store_id || form.store_id === 'null'" x-cloak
class="mb-4 p-4 bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 rounded-lg">
<h4 class="text-sm font-semibold text-blue-800 dark:text-blue-300 mb-2 flex items-center">
<span x-html="$icon('information-circle', 'w-4 h-4 mr-1.5')"></span>
Available Placeholders
</h4>
<p class="text-xs text-blue-700 dark:text-blue-400 mb-2">
Use these placeholders in store default pages. They will be automatically replaced with the store's actual information when displayed.
</p>
<div class="flex flex-wrap gap-2">
{% for name, description in placeholders %}
<code class="px-2 py-1 text-xs bg-blue-100 dark:bg-blue-800/40 text-blue-800 dark:text-blue-300 rounded cursor-pointer hover:bg-blue-200 dark:hover:bg-blue-800/60 transition-colors"
@click="navigator.clipboard.writeText('{% raw %}{{{% endraw %}{{ name }}{% raw %}}}{% endraw %}')"
title="{{ description }} — click to copy">
{% raw %}{{{% endraw %}{{ name }}{% raw %}}}{% endraw %}
</code>
{% endfor %}
</div>
<p class="text-xs text-blue-600 dark:text-blue-500 mt-2">
Click a placeholder to copy it to your clipboard.
</p>
</div>
</div>
<!-- ══════════════════════════════════════════════════════════════════ -->