feat(v3): v3 wird Default unter /antrag/{drs}, v2 zieht nach /v2/antrag/{drs}

Routen:
- /antrag/{drs}     → v3 (Standard, Bürger:innen-Modus single column)
- /v2/antrag/{drs}  → v2 (alter Profi-Modus, weiterhin erreichbar)
- /v3/antrag/{drs}  → Alias auf v3 (für alte Bookmarks)

UI-Hinweise auf alternative Ansichten ausgeblendet:
- v3-Topbar-Pill "Bürger:innen-Modus · Beta" + "→ Profi-Modus"-Toggle raus
- v2-Topbar-Link "→ Bürger:innen-Modus · v3 Beta" raus

Im Admin-Bereich (/v2/admin/stand) neuer Block "Alternative Ansichten"
mit Beispiel-Drucksache, Live-Link auf v3 (Default) und v2 (Profi).
Nur Admins sehen die Hinweise auf v2.

Trennlinien-Cleanup im Rest-Block:
- Doppellinie unter Abstimmungsergebnis aufgelöst (.v3-rest hatte
  border-top, das v3-section.border-bottom doppelt war).
- Neue Trennlinien via Klasse v3-rest-divider-top vor:
  · Teilen-Block (zwischen Ähnliche und Teilen)
  · Aktions-Links (zwischen Teilen und administrativem Bereich)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dotty Dotter 2026-05-07 11:29:06 +02:00
parent 4f4d9f3478
commit 1ef5578e02
5 changed files with 68 additions and 41 deletions

View File

@ -376,7 +376,22 @@ _MATRIX_EXPLANATIONS = {
@app.get("/antrag/{drucksache:path}", response_class=HTMLResponse)
async def antrag_detail(request: Request, drucksache: str, current_user: Optional[dict] = Depends(get_current_user)):
"""v2-Antrag-Detail (Profi-Modus): volles GWÖ-Dashboard pro Drucksache."""
"""Standard-Antrag-Detail (v3 Bürger:innen-Modus, single column).
Bisheriger Profi-Modus (v2, zwei Spalten) ist unter /v2/antrag/{drs}
weiterhin erreichbar Link im Admin-Bereich (/v2/admin/stand).
"""
return await _render_antrag_detail(
request, drucksache, current_user, "v3/screens/antrag_detail.html"
)
@app.get("/v2/antrag/{drucksache:path}", response_class=HTMLResponse)
async def antrag_detail_v2(request: Request, drucksache: str, current_user: Optional[dict] = Depends(get_current_user)):
"""Alt-Profi-Modus: zweispaltiges Layout mit allen Feldern gleichzeitig.
Ist nicht mehr Standard, bleibt aber als Power-User-Variante zugaenglich.
"""
return await _render_antrag_detail(
request, drucksache, current_user, "v2/screens/antrag_detail.html"
)
@ -384,12 +399,8 @@ async def antrag_detail(request: Request, drucksache: str, current_user: Optiona
@app.get("/v3/antrag/{drucksache:path}", response_class=HTMLResponse)
async def antrag_detail_v3(request: Request, drucksache: str, current_user: Optional[dict] = Depends(get_current_user)):
"""v3-Antrag-Detail (Bürger:innen-Modus, Beta): vereinfachte Vorschau.
Sandbox fuer Issues #184 (CD-Konformitaet) und #185 (Bürgerinnen-
Perspektive). Initial identisch zu v2; iterative Vereinfachungen
folgen pro PR. v2-Endpoint bleibt unangetastet.
"""
"""Alias auf den Standard (= /antrag/{drs}), damit alte Bookmarks
auf v3-URLs weiter funktionieren."""
return await _render_antrag_detail(
request, drucksache, current_user, "v3/screens/antrag_detail.html"
)

View File

@ -407,14 +407,24 @@
}
/* ── 9. Rest-Block ─────────────────────────────────────────────────── */
/* Kein eigenes border-top mehr die letzte v3-section (z.B.
Abstimmungsergebnis) liefert bereits die Trennlinie via border-bottom.
Vorher waren beide aktiv Doppellinie. */
.v3-rest {
margin-top: 28px;
padding-top: 20px;
border-top: 1px solid var(--hairline);
display: flex;
flex-direction: column;
gap: 22px;
}
/* Trennlinien zwischen ausgewählten Rest-Blöcken: vor Teilen und vor
Aktions-Links (Beginn des administrativen Bereichs). */
.v3-rest > .v3-rest-block.v3-rest-divider-top,
.v3-rest > .v3-rest-aktions.v3-rest-divider-top {
border-top: 1px solid var(--hairline);
padding-top: 22px;
}
.v3-rest-block {
font-size: 13px;
}

View File

@ -167,6 +167,19 @@
</div>
<div id="stand-meta" style="font-family:var(--font-mono);font-size:11px;opacity:0.5;margin-top:1.5rem;"></div>
{# Alternative Detail-Ansichten — nur im Admin sichtbar.
Default ist v3 (`/antrag/{drs}`), der frühere Profi-Modus läuft
unter /v2/antrag/. Hier ein Sample-Link, damit Admins ihn finden. #}
<h3 class="v2-h3" style="margin-top:32px;">Alternative Ansichten</h3>
<p style="font-family:var(--font-mono);font-size:12px;opacity:0.75;line-height:1.6;">
Standard-Detailansicht ist v3 (Bürger:innen-Modus, single column).
Die frühere v2-Profi-Ansicht (zwei Spalten, alle Felder gleichzeitig)
bleibt unter <code>/v2/antrag/&lt;Drucksache&gt;</code> erreichbar.
</p>
<p id="alt-views-sample" style="font-family:var(--font-mono);font-size:12px;line-height:1.8;">
Lade aktuelles Beispiel …
</p>
</div>
{% endblock %}
@ -281,6 +294,30 @@ async function loadStand() {
}
}
/* Sample-Link für Alternative Ansichten: aktuellster Antrag */
(async function () {
var el = document.getElementById('alt-views-sample');
if (!el) return;
try {
var r = await fetch('/api/assessments?limit=1');
var data = await r.json();
var a = (data && data[0]) || (data.results && data.results[0]) || null;
if (!a || !a.drucksache) {
el.textContent = 'Kein Beispiel-Antrag verfügbar.';
return;
}
var drs = encodeURIComponent(a.drucksache);
var labelFor = function (s) { return s.length > 70 ? s.slice(0,68) + '…' : s; };
el.innerHTML =
'<strong>Beispiel:</strong> ' + (a.bundesland || '?') + ' · Drs. ' + a.drucksache +
' — „' + labelFor((a.title || '')) + '"<br>' +
<a href="/antrag/' + drs + '">Standard (v3, Bürger:innen-Modus)</a><br>' +
<a href="/v2/antrag/' + drs + '">v2 — Profi-Modus, zwei Spalten</a>';
} catch (e) {
el.textContent = 'Konnte Beispiel-Link nicht laden.';
}
})();
loadStand();
let _standInterval = setInterval(loadStand, 30000);
// Pause-Polling wenn Tab versteckt (#183).

View File

@ -640,20 +640,6 @@ document.addEventListener('keydown', function (e) {
}
});
/* v3-Toggle in der Topbar: nur bei vorhandenem Antrag, Link auf
/v3/antrag/<drs>. Injektion via JS, damit v2/base.html unangetastet bleibt. */
{% if antrag is defined and antrag and antrag.drucksache %}
(function () {
var bar = document.querySelector('.v2-topbar');
if (!bar) return;
var link = document.createElement('a');
link.href = '/v3/antrag/' + encodeURIComponent({{ antrag.drucksache | tojson }});
link.textContent = '→ Bürger:innen-Modus · v3 Beta';
link.title = 'Vereinfachte Vorschau für Erst-Leser:innen (v3 Beta).';
link.style.marginLeft = 'auto';
bar.appendChild(link);
})();
{% endif %}
</script>
{# Matrix-Erklärungen als JSON in den Browser übertragen #}

View File

@ -366,7 +366,7 @@
</div>
{# Teilen #}
<div class="v3-rest-block">
<div class="v3-rest-block v3-rest-divider-top">
<h3 class="v3-h3">Teilen</h3>
<div class="v3-share-buttons">
<button onclick="v2DetailShareCopy()" class="v3-action-btn">📋 Kopieren</button>
@ -379,7 +379,7 @@
</div>
{# Aktions-Links: PDF, Original, JSON, Permalink — unter Teilen #}
<div class="v3-rest-aktions">
<div class="v3-rest-aktions v3-rest-divider-top">
<a href="/api/assessment/pdf?drucksache={{ antrag.drucksache | urlencode }}">PDF-Bericht</a>
{% if antrag.link %}
<a href="{{ antrag.link }}" target="_blank" rel="noopener">Original-Antrag (Landtag)</a>
@ -470,23 +470,6 @@
{% block body_scripts %}
{{ super() }}
<script>
/* v3-Topbar-Pill + Toggle zurueck zu Profi-Modus */
(function () {
var bar = document.querySelector('.v2-topbar');
if (!bar) return;
var pill = document.createElement('span');
pill.className = 'v3-beta-badge';
pill.textContent = 'Bürger:innen-Modus · Beta';
pill.title = 'Vereinfachte Ansicht. v3 ist eine frühe Vorschau.';
var toggle = document.createElement('a');
toggle.className = 'v3-modus-toggle';
toggle.href = '/antrag/' + encodeURIComponent({{ antrag.drucksache | tojson if antrag is defined and antrag else '""' | safe }});
toggle.textContent = '→ Profi-Modus';
toggle.title = 'Volle GWÖ-Detailansicht (v2) öffnen';
bar.appendChild(pill);
bar.appendChild(toggle);
})();
/* Ähnliche Anträge: /api/assessment/similar?drucksache=…&top_k=5 */
(async function () {
var box = document.getElementById('v3-similar-box');