Publisert tittel, ingress, OG-bilde og undertittel er nå egne noder koblet til artikler via title/subtitle/summary/og_image-edges. Rendering bruker presentasjonselementer med fallback til artikkelfelt. Backend: - Ny query: GET /query/presentation_elements?article_id=... - render_article_to_cas henter presentasjonselementer via edges - fetch_article + fetch_index_articles bruker pres.elementer - Batch-henting for forsideartikler (én SQL-spørring) - ArticleData utvides med subtitle + og_image - Alle fire temaer viser subtitle og OG-bilde - SEO og_image-tag fylles fra presentasjonselement Frontend: - PresentationEditor.svelte: opprett/rediger tittel, undertittel, ingress, OG-bilde med variantvelger (editorial/ai/social/rss) - Integrert i PublishDialog via <details>-seksjon - API-klient: fetchPresentationElements(), deleteNode() Grunnlag for A/B-testing (oppgave 14.17): edge-metadata støtter ab_status/impressions/clicks/ctr, best_of() prioriterer winner > testing. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
60 lines
2.2 KiB
HTML
60 lines
2.2 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}{{ article.title }} — {{ collection_title }}{% endblock %}
|
|
|
|
{% block seo %}
|
|
<meta name="description" content="{{ seo.description }}">
|
|
<link rel="canonical" href="{{ seo.canonical_url }}">
|
|
<meta property="og:type" content="article">
|
|
<meta property="og:title" content="{{ seo.og_title }}">
|
|
<meta property="og:description" content="{{ seo.description }}">
|
|
<meta property="og:url" content="{{ seo.canonical_url }}">
|
|
<meta property="og:site_name" content="{{ collection_title }}">
|
|
{% if seo.og_image %}<meta property="og:image" content="{{ seo.og_image }}">{% endif %}
|
|
<meta property="article:published_time" content="{{ article.published_at }}">
|
|
<link rel="alternate" type="application/atom+xml" title="{{ collection_title }} — RSS" href="{{ base_url }}/feed.xml">
|
|
<script type="application/ld+json">{{ seo.json_ld | safe }}</script>
|
|
{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
.blog-article {
|
|
max-width: var(--layout-max-width);
|
|
margin: 2rem auto;
|
|
padding: 0 1rem;
|
|
}
|
|
.blog-article__title {
|
|
font-family: var(--font-heading);
|
|
font-size: 2rem;
|
|
line-height: 1.2;
|
|
color: var(--color-primary);
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
.blog-article__meta {
|
|
color: var(--color-muted);
|
|
font-size: 0.875rem;
|
|
margin-bottom: 2rem;
|
|
}
|
|
.blog-article__content {
|
|
font-size: 1.05rem;
|
|
line-height: 1.75;
|
|
}
|
|
.blog-article__content p { margin-bottom: 1em; }
|
|
.blog-article__back {
|
|
display: inline-block;
|
|
margin-top: 2rem;
|
|
font-size: 0.9rem;
|
|
}
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<article class="blog-article">
|
|
{% if article.og_image %}<img src="/cas/{{ article.og_image }}" alt="{{ article.title }}" style="width:100%;max-height:400px;object-fit:cover;border-radius:0.5rem;margin-bottom:1.5rem;">{% endif %}
|
|
<h1 class="blog-article__title">{{ article.title }}</h1>
|
|
{% if article.subtitle %}<p style="font-size:1.2rem;color:var(--color-muted);margin-bottom:0.5rem;font-family:var(--font-heading);">{{ article.subtitle }}</p>{% endif %}
|
|
<div class="blog-article__meta">{{ article.published_at_short }}</div>
|
|
<div class="blog-article__content">
|
|
{{ article.content | safe }}
|
|
</div>
|
|
<a class="blog-article__back" href="{{ base_url }}">← Tilbake</a>
|
|
</article>
|
|
{% endblock %}
|