From 8c4a4078f9e92f3d41bd8e4f518b0e874061a7cb Mon Sep 17 00:00:00 2001 From: Deeman Date: Sat, 28 Feb 2026 21:34:07 +0100 Subject: [PATCH] fix(affiliate): live preview uses dedicated /affiliate/preview endpoint The form was posting to the save route on every input change (which would save the product on every keystroke). Added a dedicated POST /admin/affiliate/preview route that renders the product_card.html partial from form data without touching the database. Form now keeps action pointing to the save route; an invisible hx-div triggers preview-only POSTs via hx-include="#affiliate-form". Co-Authored-By: Claude Sonnet 4.6 --- web/src/padelnomics/admin/routes.py | 25 +++++++++++++++++++ .../admin/templates/admin/affiliate_form.html | 12 ++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/web/src/padelnomics/admin/routes.py b/web/src/padelnomics/admin/routes.py index 2afb389..84ef4d4 100644 --- a/web/src/padelnomics/admin/routes.py +++ b/web/src/padelnomics/admin/routes.py @@ -3373,6 +3373,31 @@ async def affiliate_results(): ) +@bp.route("/affiliate/preview", methods=["POST"]) +@role_required("admin") +@csrf_protect +async def affiliate_preview(): + """Render a product card fragment from form data — used by live preview HTMX.""" + from ..content.routes import _bake_env + from ..i18n import get_translations + + form = await request.form + data = _form_to_product(form) + lang = data["language"] or "de" + + # Convert JSON-string pros/cons to lists for the template + product = dict(data) + product["pros"] = json.loads(product["pros"]) if product["pros"] else [] + product["cons"] = json.loads(product["cons"]) if product["cons"] else [] + + if not product["name"]: + return "

Fill in the form to see a preview.

" + + tmpl = _bake_env.get_template("partials/product_card.html") + html = tmpl.render(product=product, t=get_translations(lang), lang=lang) + return html + + @bp.route("/affiliate/new", methods=["GET", "POST"]) @role_required("admin") @csrf_protect diff --git a/web/src/padelnomics/admin/templates/admin/affiliate_form.html b/web/src/padelnomics/admin/templates/admin/affiliate_form.html index 35302c6..b17c7d3 100644 --- a/web/src/padelnomics/admin/templates/admin/affiliate_form.html +++ b/web/src/padelnomics/admin/templates/admin/affiliate_form.html @@ -39,11 +39,15 @@ document.addEventListener('DOMContentLoaded', function() {
{# ── Left: form ── #} + {# Invisible trigger: fires preview on any input change, includes the whole form #} +
+
+ action="{% if editing %}{{ url_for('admin.affiliate_edit', product_id=product_id) }}{% else %}{{ url_for('admin.affiliate_new') }}{% endif %}">