gwoe-antragspruefer/app/templates/v2/screens/feed.html
Dotty Dotter 7cbd46f88d feat(v2): Atom-Feed-Konfig-Seite + Eigene-Abos-Verwaltung
Backend (Filter sind seit jeher da):
- /api/feed.xml?bundesland=&partei=&limit=
- /api/subscriptions GET/POST/DELETE

UI:
- /v2/feed: Form mit BL/Partei/Limit, generiert Feed-URL live, Buttons Oeffnen/
  URL-Kopieren/In-Feedly. Default-BL aus Header-Selektor uebernommen
- /v2/abos: Liste eigener Abos + Form zum Anlegen/Loeschen, BL-Dropdown,
  Partei-Freitext, Frequenz daily/weekly
- Sidebar 'Daten'-Gruppe um beide Eintraege erweitert (statt Direkt-Link auf
  /api/feed.xml)
- Beide Routen mit Depends(require_auth) — Anonyme bekommen 401-Redirect

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 22:34:55 +02:00

123 lines
5.2 KiB
HTML

{% extends "v2/base.html" %}
{% block title %}Atom-Feed — GWÖ-Antragsprüfer{% endblock %}
{% set v2_active_nav = "feed" %}
{% block head_extra %}
<style>
.feed-form {
display: grid; grid-template-columns: max-content 1fr; gap: 8px 14px;
align-items: center; margin-bottom: 24px; max-width: 560px;
padding: 14px; border: 1px solid var(--ecg-border); border-radius: 4px;
background: var(--ecg-bg-subtle);
}
.feed-form label { font-family: var(--font-mono); font-size: 11px;
text-transform: uppercase; letter-spacing: 0.06em; opacity: 0.7; }
.feed-form select, .feed-form input {
font-family: var(--font-mono); font-size: 13px;
padding: 6px 8px; border: 1px solid var(--ecg-border);
border-radius: 4px; background: var(--ecg-card-bg); color: var(--ecg-dark);
}
.feed-url-box {
margin-top: 16px; padding: 14px; border: 1px solid var(--ecg-border);
border-radius: 4px; background: var(--ecg-card-bg);
}
.feed-url {
font-family: var(--font-mono); font-size: 12px; padding: 8px 10px;
border: 1px solid var(--ecg-border); border-radius: 3px; word-break: break-all;
background: var(--paper); color: var(--ecg-dark); display: block;
margin: 8px 0;
}
.feed-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.feed-btn {
font-family: var(--font-mono); font-size: 12px; padding: 6px 14px;
background: var(--ecg-teal); color: #fff; border: none; border-radius: 3px;
cursor: pointer; text-decoration: none; display: inline-flex;
align-items: center; gap: 6px;
}
.feed-btn.secondary { background: none; color: var(--ecg-dark); border: 1px solid var(--ecg-border); }
</style>
{% endblock %}
{% block main %}
<div style="padding:0 0 1rem;">
<h1 style="font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;">Atom-Feed</h1>
<p style="font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.6;">
Konfigurierbarer Feed der neuesten Bewertungen — abonnierbar mit jedem RSS/Atom-Reader.
</p>
</div>
<form class="feed-form" onsubmit="event.preventDefault();feedUpdate();">
<label for="feed-bl">Bundesland</label>
<select id="feed-bl" onchange="feedUpdate()">
<option value="">— alle —</option>
{% for bl in v2_bundeslaender %}<option value="{{ bl.code }}">{{ bl.code }} — {{ bl.name }}</option>{% endfor %}
</select>
<label for="feed-partei">Partei</label>
<input type="text" id="feed-partei" placeholder="z.B. SPD (leer = alle)" oninput="feedUpdate()" autocomplete="off">
<label for="feed-limit">Anzahl</label>
<input type="number" id="feed-limit" min="1" max="200" value="50" oninput="feedUpdate()">
</form>
<div class="feed-url-box">
<div style="font-family:var(--font-mono);font-size:11px;text-transform:uppercase;letter-spacing:0.07em;opacity:0.7;">Feed-URL</div>
<code class="feed-url" id="feed-url">/api/feed.xml</code>
<div class="feed-actions">
<a id="feed-open" href="/api/feed.xml" class="feed-btn" target="_blank" rel="noopener">📰 Öffnen</a>
<button class="feed-btn secondary" onclick="feedCopy()">📋 URL kopieren</button>
<a id="feed-reader" href="" class="feed-btn secondary" target="_blank" rel="noopener" title="In Feedly öffnen">In Feedly</a>
</div>
</div>
<div style="margin-top:24px;font-size:12px;color:var(--ecg-dark);opacity:0.7;line-height:1.6;max-width:600px;">
<p><strong>Hinweis:</strong> Du kannst die Feed-URL in jedem RSS-Reader (z.B. Feedly, NewsBlur, Inoreader, NetNewsWire, Thunderbird) abonnieren. Der Feed ist Atom 1.0 und liefert die letzten Bewertungen mit Score, Empfehlung und Kurzbegründung.</p>
<p>Wenn du regelmäßige Mails statt Pull-Feed willst, lege ein <a href="/v2/abos" style="color:var(--ecg-teal);">E-Mail-Abo</a> an.</p>
</div>
{% endblock %}
{% block body_scripts %}
<script>
function feedUpdate() {
var bl = document.getElementById('feed-bl').value;
var part = document.getElementById('feed-partei').value.trim();
var limit = document.getElementById('feed-limit').value;
var qs = [];
if (bl) qs.push('bundesland=' + encodeURIComponent(bl));
if (part) qs.push('partei=' + encodeURIComponent(part));
if (limit && limit !== '50') qs.push('limit=' + encodeURIComponent(limit));
var path = '/api/feed.xml' + (qs.length ? ('?' + qs.join('&')) : '');
var full = location.origin + path;
document.getElementById('feed-url').textContent = full;
document.getElementById('feed-open').href = path;
document.getElementById('feed-reader').href = 'https://feedly.com/i/subscription/feed%2F' + encodeURIComponent(full);
}
async function feedCopy() {
var url = document.getElementById('feed-url').textContent;
if (navigator.clipboard) await navigator.clipboard.writeText(url);
else { prompt('Kopieren:', url); }
}
// Bundesland aus globaler Auswahl als Default übernehmen
document.addEventListener('DOMContentLoaded', function() {
var globalBl = (window.v2GetGlobalBl && window.v2GetGlobalBl()) || 'ALL';
if (globalBl && globalBl !== 'ALL') {
var sel = document.getElementById('feed-bl');
if (sel) sel.value = globalBl;
}
feedUpdate();
});
window.addEventListener('v2-bl-changed', function(e) {
var sel = document.getElementById('feed-bl');
if (sel) {
sel.value = (e.detail && e.detail.bl !== 'ALL') ? e.detail.bl : '';
feedUpdate();
}
});
</script>
{% endblock %}