diff --git a/web/src/padelnomics/admin/routes.py b/web/src/padelnomics/admin/routes.py index 32bf36d..b60d27f 100644 --- a/web/src/padelnomics/admin/routes.py +++ b/web/src/padelnomics/admin/routes.py @@ -33,6 +33,7 @@ from ..core import ( utcnow, utcnow_iso, ) +from ..email_templates import EMAIL_TEMPLATE_REGISTRY, render_email_template logger = logging.getLogger(__name__) @@ -1398,10 +1399,16 @@ async def email_compose(): email_addresses=EMAIL_ADDRESSES, ) - html = f"

{body.replace(chr(10), '
')}

" + body_html = f"

{body.replace(chr(10), '
')}

" if wrap: - from ..worker import _email_wrap - html = _email_wrap(html) + html = render_email_template( + "emails/admin_compose.html", + lang="en", + body_html=body_html, + preheader="", + ) + else: + html = body_html result = await send_email( to=to, subject=subject, html=html, @@ -1424,6 +1431,36 @@ async def email_compose(): ) +@bp.route("/emails/compose/preview", methods=["POST"]) +@role_required("admin") +async def compose_preview(): + """HTMX endpoint: render live preview for compose textarea (no CSRF — read-only).""" + form = await request.form + body = form.get("body", "").strip() + wrap = form.get("wrap", "") == "1" + + body_html = f"

{body.replace(chr(10), '
')}

" if body else "" + + if wrap and body_html: + try: + rendered_html = render_email_template( + "emails/admin_compose.html", + lang="en", + body_html=body_html, + preheader="", + ) + except Exception: + logger.exception("compose_preview: template render failed") + rendered_html = body_html + else: + rendered_html = body_html + + return await render_template( + "admin/partials/email_preview_frame.html", + rendered_html=rendered_html, + ) + + # --- Audiences --- @bp.route("/emails/audiences") diff --git a/web/src/padelnomics/admin/templates/admin/email_compose.html b/web/src/padelnomics/admin/templates/admin/email_compose.html index dfd5a7a..1ab8846 100644 --- a/web/src/padelnomics/admin/templates/admin/email_compose.html +++ b/web/src/padelnomics/admin/templates/admin/email_compose.html @@ -2,51 +2,91 @@ {% set admin_page = "compose" %} {% block title %}Compose Email - Admin - {{ config.APP_NAME }}{% endblock %} +{% block admin_head %} + +{% endblock %} + {% block admin_content %}
← Sent Log

Compose Email

-
-
- +
+ {# ── Left: form ────────────────────────────────────── #} +
+
+ + -
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- - -
+
+ + +
-
- -
+
+ +
-
- - Cancel +
+ + Cancel +
+
- +
+ + {# ── Right: live preview panel ─────────────────────── #} +
+
Live preview
+
+

Start typing to see a preview…

+
+
{% endblock %} diff --git a/web/src/padelnomics/admin/templates/admin/partials/email_preview_frame.html b/web/src/padelnomics/admin/templates/admin/partials/email_preview_frame.html new file mode 100644 index 0000000..5943fe3 --- /dev/null +++ b/web/src/padelnomics/admin/templates/admin/partials/email_preview_frame.html @@ -0,0 +1,8 @@ +{# HTMX partial: sandboxed iframe showing a rendered email preview. + Rendered by POST /admin/emails/compose/preview. #} + diff --git a/web/src/padelnomics/templates/emails/admin_compose.html b/web/src/padelnomics/templates/emails/admin_compose.html new file mode 100644 index 0000000..657ad07 --- /dev/null +++ b/web/src/padelnomics/templates/emails/admin_compose.html @@ -0,0 +1,5 @@ +{% extends "emails/_base.html" %} + +{% block body %} +{{ body_html | safe }} +{% endblock %}