diff --git a/web/src/padelnomics/admin/routes.py b/web/src/padelnomics/admin/routes.py index 71fd52b..47b3765 100644 --- a/web/src/padelnomics/admin/routes.py +++ b/web/src/padelnomics/admin/routes.py @@ -2556,7 +2556,7 @@ async def article_new(): await flash(f"Article '{title}' created.", "success") return redirect(url_for("admin.articles")) - return await render_template("admin/article_form.html", data={}, editing=False) + return await render_template("admin/article_form.html", data={}, editing=False, body_html="") @bp.route("/articles//edit", methods=["GET", "POST"]) @@ -2626,18 +2626,41 @@ async def article_edit(article_id: int): # Load markdown source if available (manual or generated) from ..content.routes import BUILD_DIR as CONTENT_BUILD_DIR - md_path = Path("data/content/articles") / f"{article['slug']}.md" + md_path = _ARTICLES_DIR / f"{article['slug']}.md" if not md_path.exists(): lang = article["language"] or "en" md_path = CONTENT_BUILD_DIR / lang / "md" / f"{article['slug']}.md" - body = md_path.read_text() if md_path.exists() else "" + raw = md_path.read_text() if md_path.exists() else "" + # Strip YAML frontmatter so only the prose body appears in the editor + m = _FRONTMATTER_RE.match(raw) + body = raw[m.end():].lstrip("\n") if m else raw + + body_html = mistune.html(body) if body else "" data = {**dict(article), "body": body} return await render_template( - "admin/article_form.html", data=data, editing=True, article_id=article_id, + "admin/article_form.html", + data=data, + editing=True, + article_id=article_id, + body_html=body_html, ) +@bp.route("/articles/preview", methods=["POST"]) +@role_required("admin") +@csrf_protect +async def article_preview(): + """Render markdown body to HTML for the live editor preview panel.""" + from markupsafe import Markup + form = await request.form + body = form.get("body", "") + m = _FRONTMATTER_RE.match(body) + body = body[m.end():].lstrip("\n") if m else body + body_html = Markup(mistune.html(body)) if body else "" + return await render_template("admin/partials/article_preview.html", body_html=body_html) + + @bp.route("/articles//delete", methods=["POST"]) @role_required("admin") @csrf_protect diff --git a/web/src/padelnomics/admin/templates/admin/article_form.html b/web/src/padelnomics/admin/templates/admin/article_form.html index 9e7df00..d0107e8 100644 --- a/web/src/padelnomics/admin/templates/admin/article_form.html +++ b/web/src/padelnomics/admin/templates/admin/article_form.html @@ -1,89 +1,422 @@ {% extends "admin/base_admin.html" %} {% set admin_page = "articles" %} -{% block title %}{% if editing %}Edit{% else %}New{% endif %} Article - Admin - {{ config.APP_NAME }}{% endblock %} +{% block title %}{% if editing %}Edit{% else %}New{% endif %} Article — Admin — {{ config.APP_NAME }}{% endblock %} + +{% block head %}{{ super() }} + +{% endblock %} {% block admin_content %} -
- ← Back to articles -

{% if editing %}Edit{% else %}New{% endif %} Article

+
-
- + +
+ ← Articles +
+ + {% if editing %}{{ data.get('title', 'Edit Article') }}{% else %}New Article{% endif %} + + {% if editing %} + + {{ data.get('status', 'draft') }} + + {% endif %} + +
-
-
- - + + + + + +
+
+
+ +
-
- - +
+ +
-
- -
- - -

Defaults to /slug. Must not conflict with existing routes.

-
- -
- - -
- -
-
- - +
+ +
-
- - -
-
- - -
-
- -
- - -

Use [scenario:slug] to embed scenario widgets. Sections: :capex, :operating, :cashflow, :returns, :full

-
- -
-
- - + +
-
- - +
-
- +
+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ -

Leave blank for now. Future date = scheduled.

+ value="{{ data.get('published_at', '')[:16] if data.get('published_at') else '' }}"> +
+
+
+ + +
+ + +
+
+ Markdown + [scenario:slug] · [product:slug] +
+ +
+ + +
+
+ Preview + Rendering… +
+
+
+ {% if body_html %} +
{{ body_html }}
+ {% else %} +

Start writing to see a preview.

+ {% endif %} +
- - -
+
+ + +
{% endblock %} diff --git a/web/src/padelnomics/admin/templates/admin/partials/article_preview.html b/web/src/padelnomics/admin/templates/admin/partials/article_preview.html new file mode 100644 index 0000000..92fba48 --- /dev/null +++ b/web/src/padelnomics/admin/templates/admin/partials/article_preview.html @@ -0,0 +1,5 @@ +{% if body_html %} +
{{ body_html }}
+{% else %} +

Start writing to see a preview.

+{% endif %}