gwoe-antragspruefer/app/templates/v2/screens/durchsuchen.html
Dotty Dotter c3fd617585 ui(tour): permanenter Tour-Link in der Topbar + modernes Button-Styling
User-Feedback: Tour-Start muss auch nach dem ersten Mal möglich sein,
und die Buttons sahen "sehr 90er" aus.

Permanenter Tour-Zugang:
- Topbar-Link "🧭 Tour" neben Methodik/Quellen, sichtbar auf jeder
  Page mit ``window.GWOE_TOUR_STEPS`` (Antrag-Detail + Startseite).
- Per JS in base.html nach DOMContentLoaded sichtbar geschaltet.
- Style: dezente teal-Pille, kein dominanter Button.

Button-Modernisierung:
- ``.v2-chip`` und ``.v3-action-btn`` jetzt pill-shaped (border-radius
  999px statt 3px), mit transition + box-shadow on hover und subtle
  transform on active. Focus-visible mit klarem outline.
- Primary-Variante: teal-solid mit weichem Drop-Shadow, nicht das alte
  dark-Mode-Schwarz.
- Welcome-Banner (Startseite): scharfes ``v2-kasten outline-blue`` weg,
  stattdessen weicher gradient-Hintergrund teal→blue mit border 22%
  teal-Hauch und subtle box-shadow. Skip-Button als Text-Link
  ("Später") statt vollwertiger Chip — visuell klar nachgeordnet.
2026-05-09 03:31:09 +02:00

444 lines
17 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{% extends "v2/base.html" %}
{% from "v2/components/result_row.html" import result_row %}
{% from "v2/components/chip.html" import chip %}
{% block title %}Durchsuchen — GWÖ-Antragsprüfer{% endblock %}
{% set v2_active_nav = "durchsuchen" %}
{% block main %}
{# ── Welcome-Banner: Tour-Einstieg für Erst-Besucher:innen (#185) ── #}
<aside id="gwoe-welcome-banner" class="gwoe-welcome-banner" hidden role="region"
aria-label="Willkommen — Tour-Einstieg">
<span aria-hidden="true" class="gwoe-welcome-icon">🧭</span>
<div class="gwoe-welcome-text">
<strong>Du bist neu hier?</strong>
Soll ich dir die Seite kurz erklären? Geführte Tour mit Sprachausgabe — jederzeit abbrechbar.
</div>
<div class="gwoe-welcome-actions">
<button type="button" onclick="gwoeTourStart()" class="v2-chip primary"
id="gwoe-welcome-start">Tour starten</button>
<button type="button" onclick="gwoeWelcomeDismiss()" class="gwoe-welcome-skip"
id="gwoe-welcome-dismiss" aria-label="Banner schließen">Später</button>
</div>
</aside>
{# ── Toolbar: Suche ──────────────────────────────────────────────── #}
{# BL-Filter läuft jetzt über den globalen Selector in der Topbar. #}
<div class="v2-toolbar" id="v2-toolbar" role="toolbar" aria-label="Filter und Suche">
<input class="v2-search"
type="search"
placeholder="Anträge durchsuchen …"
aria-label="Anträge durchsuchen"
id="v2-search-input">
</div>
{# ── Score-Filter + Sortierung ───────────────────────────────────── #}
<div class="v2-toolbar" role="toolbar" aria-label="Score-Filter und Sortierung" style="border:0;padding:4px 0;margin:0 0 8px;position:static;">
<button class="v2-chip active" data-band="ALL" onclick="v2SetBand(this,'ALL')">Alle Scores</button>
<button class="v2-chip" data-band="HIGH" onclick="v2SetBand(this,'HIGH')">Score 810</button>
<button class="v2-chip" data-band="MID" onclick="v2SetBand(this,'MID')">57</button>
<button class="v2-chip" data-band="LOW" onclick="v2SetBand(this,'LOW')">04</button>
<span class="v2-toolbar-sep"></span>
<select id="v2-sort"
aria-label="Sortierung"
onchange="v2ApplySort(this.value)"
style="padding:4px 8px;border:1px solid var(--hairline);border-radius:4px;
font-family:var(--font-mono);font-size:11px;background:var(--surface);
color:var(--ecg-dark);cursor:pointer;">
<option value="score-desc">Score ↓</option>
<option value="score-asc">Score ↑</option>
<option value="date-desc">Datum ↓</option>
<option value="date-asc">Datum ↑</option>
<option value="drucksache-desc">Drs.-Nr. ↓</option>
<option value="drucksache-asc">Drs.-Nr. ↑</option>
<option value="title-asc">Titel AZ</option>
<option value="title-desc">Titel ZA</option>
</select>
</div>
{# ── Ergebnisliste ───────────────────────────────────────────────── #}
<div id="v2-results" role="list">
{% if assessments %}
{% for a in assessments %}
{{ result_row(a) }}
{% endfor %}
{% else %}
<p style="font-family:var(--font-mono);font-size:13px;color:var(--ecg-dark);opacity:0.6;margin-top:32px;">
Noch keine Bewertungen in der Datenbank.
</p>
{% endif %}
</div>{# #v2-results #}
{# ── Empty-State ─────────────────────────────────────────────────── #}
<div id="v2-empty-state" class="v2-kasten outline-green" style="display:none;margin-top:32px;">
<h4>Keine Ergebnisse</h4>
<p>Die aktuelle Filterauswahl liefert keine Treffer.
<button onclick="v2ResetFilters()"
style="background:none;border:none;color:var(--ecg-green);cursor:pointer;font-weight:900;text-decoration:underline;font-size:inherit;font-family:inherit;">
Filter zurücksetzen
</button>
</p>
</div>
{# ── Tour-Stationen für die Startseite + Engine-Include ──────────── #}
<script>
window.GWOE_TOUR_STEPS = [
{ selector: '.v2-brand-link, .v2-brand',
title: 'Willkommen beim GWÖ-Antragsprüfer',
text: 'Diese Seite bewertet Anträge aus deutschen Parlamenten nach der Gemeinwohl-Matrix. Was sie auszeichnet: Sie schaut nicht nur, ob ein Antrag „gut klingt", sondern wie sehr er fünf Werten dient — Würde, Solidarität, Nachhaltigkeit, Gerechtigkeit und Demokratie.' },
{ selector: '#v2-search-input',
title: 'Anträge durchsuchen',
text: 'Hier findest du Anträge nach Stichwort, Drucksachen-Nummer oder Antragstellerin. Die Liste filtert sich beim Tippen mit.' },
{ selector: '.v2-toolbar:nth-of-type(2), [data-band]',
title: 'Filter nach Note',
text: 'Mit diesen Knöpfen filterst du nach Gemeinwohl-Note. Acht bis Zehn sind vorbildlich, fünf bis sieben durchwachsen, null bis vier problematisch. Daneben kannst du nach Datum, Score oder Titel sortieren.' },
{ selector: '#v2-results .v2-result-row, #v2-results',
title: 'Die Antrags-Liste',
text: 'Jede Karte zeigt einen Antrag mit seiner Note. Klick auf eine Karte öffnet die ausführliche Bewertung — dort wirst du dort dann auch noch eine eigene Tour finden, die das Detail erklärt.' },
{ selector: '.v2-nav-group, #v2-sidebar nav',
title: 'Navigation links',
text: 'Links findest du weitere Sichten. „Auswertungen" zeigt Aggregate über alle Anträge, „Stimmverhalten" die Konsistenz jeder Fraktion zwischen Wahlprogramm und tatsächlicher Stimme, „Quellen" alle indizierten Wahl- und Grundsatzprogramme — semantisch durchsuchbar.' },
];
</script>
{% include "v3/components/tour.html" %}
{% endblock %}
{% block body_scripts %}
<script>
/* ── v2 Listenfilter ─────────────────────────────────────────────── */
(function () {
var activeBl = 'ALL';
var activeBand = 'ALL';
function getRows() {
return document.querySelectorAll('#v2-results .v2-result-row');
}
function getScore(row) {
var cell = row.querySelector('.v2-score-cell');
return cell ? parseFloat(cell.textContent) || 0 : 0;
}
function getBl(row) {
var state = row.querySelector('.v2-r-state');
return state ? state.textContent.trim().split('·')[0].trim() : '';
}
function applyFilters() {
var q = (document.getElementById('v2-search-input').value || '').toLowerCase().trim();
var rows = getRows();
var visible = 0;
rows.forEach(function (row) {
var score = getScore(row);
var bl = getBl(row);
var text = row.textContent.toLowerCase();
var blOk = (activeBl === 'ALL') || (bl === activeBl);
var bandOk = (activeBand === 'ALL') ||
(activeBand === 'HIGH' && score >= 8) ||
(activeBand === 'MID' && score >= 5 && score < 8) ||
(activeBand === 'LOW' && score < 5);
var qOk = !q || text.includes(q);
var show = blOk && bandOk && qOk;
row.style.display = show ? '' : 'none';
if (show) visible++;
});
var empty = document.getElementById('v2-empty-state');
if (empty) empty.style.display = (visible === 0) ? 'block' : 'none';
}
/* BL-Filter: globaler Selector in der Topbar */
window.addEventListener('v2-bl-changed', function (e) {
activeBl = (e.detail && e.detail.bl) ? e.detail.bl : 'ALL';
applyFilters();
});
window.v2SetBand = function (btn, band) {
activeBand = band;
document.querySelectorAll('[data-band]').forEach(function (b) {
b.classList.toggle('active', b.dataset.band === band);
});
applyFilters();
};
window.v2ResetFilters = function () {
document.getElementById('v2-search-input').value = '';
// BL auf ALL zurücksetzen: globalen Selector aktualisieren
var sel = document.getElementById('v2-global-bl');
if (sel) sel.value = 'ALL';
window.v2SetGlobalBl && window.v2SetGlobalBl('ALL');
v2SetBand(null, 'ALL');
};
document.addEventListener('DOMContentLoaded', function () {
var input = document.getElementById('v2-search-input');
if (input) input.addEventListener('input', applyFilters);
// Gespeicherten BL-Wert beim Laden anwenden
activeBl = (window.v2GetGlobalBl && window.v2GetGlobalBl()) || 'ALL';
applyFilters();
});
})();
/* ── Sort ────────────────────────────────────────────────────────── */
(function () {
var SORT_KEY = 'gwoe.v2-sort';
function getRowVal(row, field) {
if (field === 'score') {
var cell = row.querySelector('.v2-score-cell');
return cell ? parseFloat(cell.textContent) || 0 : 0;
}
if (field === 'title') {
var titleEl = row.querySelector('.v2-r-title');
return titleEl ? titleEl.textContent.trim().toLowerCase() : '';
}
if (field === 'date') {
var dateEl = row.querySelector('.v2-r-date');
return dateEl ? dateEl.textContent.trim() : '';
}
if (field === 'drucksache') {
var stateEl = row.querySelector('.v2-r-state');
return stateEl ? stateEl.textContent.trim() : '';
}
return '';
}
function sortRows(sortVal) {
var parts = sortVal.split('-');
var field = parts.slice(0, -1).join('-');
var dir = parts[parts.length - 1]; // 'asc' or 'desc'
var container = document.getElementById('v2-results');
if (!container) return;
var rows = Array.from(container.querySelectorAll('.v2-result-row'));
rows.sort(function(a, b) {
var va = getRowVal(a, field);
var vb = getRowVal(b, field);
var cmp;
if (typeof va === 'number' && typeof vb === 'number') {
cmp = va - vb;
} else {
cmp = String(va).localeCompare(String(vb), 'de');
}
return dir === 'asc' ? cmp : -cmp;
});
rows.forEach(function(row) { container.appendChild(row); });
}
window.v2ApplySort = function(val) {
try { localStorage.setItem(SORT_KEY, val); } catch (_) {}
sortRows(val);
};
document.addEventListener('DOMContentLoaded', function() {
var savedSort;
try { savedSort = localStorage.getItem(SORT_KEY); } catch (_) {}
var sortSelect = document.getElementById('v2-sort');
if (savedSort && sortSelect) {
sortSelect.value = savedSort;
sortRows(savedSort);
}
});
})();
/* ── Keyboard Shortcuts (#116 port to v2) ────────────────────────── */
(function () {
document.addEventListener('keydown', function (e) {
// Nicht in Input-Feldern auslösen
if (e.target.matches('input, textarea, select')) return;
var rows = Array.from(document.querySelectorAll('#v2-results .v2-result-row'));
var active = document.querySelector('#v2-results .v2-result-row.v2-kb-active');
var idx = active ? rows.indexOf(active) : -1;
switch (e.key) {
case 'j': // nächster Eintrag
e.preventDefault();
if (!rows.length) break;
idx = Math.min(idx + 1, rows.length - 1);
if (active) active.classList.remove('v2-kb-active');
rows[idx].classList.add('v2-kb-active');
rows[idx].scrollIntoView({ block: 'nearest' });
break;
case 'k': // vorheriger Eintrag
e.preventDefault();
if (!rows.length) break;
idx = Math.max(idx - 1, 0);
if (active) active.classList.remove('v2-kb-active');
rows[idx].classList.add('v2-kb-active');
rows[idx].scrollIntoView({ block: 'nearest' });
break;
case 'Enter': // Eintrag öffnen
if (active) {
e.preventDefault();
var href = active.getAttribute('href');
if (href) window.location.href = href;
}
break;
case '/': // Suche fokussieren
e.preventDefault();
var searchInput = document.getElementById('v2-search-input');
if (searchInput) searchInput.focus();
break;
case '?': // Shortcuts-Hilfe
e.preventDefault();
var modal = document.getElementById('v2-kb-help-modal');
if (modal) {
modal.style.display = modal.style.display === 'flex' ? 'none' : 'flex';
}
break;
case 'Escape': // Hilfe-Modal schließen (auf Detailseiten: history.back() — dort eigener Handler)
var helpModal = document.getElementById('v2-kb-help-modal');
if (helpModal && helpModal.style.display === 'flex') {
e.preventDefault();
helpModal.style.display = 'none';
}
break;
}
});
})();
</script>
{# ── Keyboard-Hilfe-Modal ───────────────────────────────────────── #}
<div id="v2-kb-help-modal"
role="dialog" aria-modal="true" aria-label="Tastenkürzel"
style="display:none;position:fixed;inset:0;background:rgba(0,0,0,0.45);
z-index:9000;align-items:center;justify-content:center;"
onclick="if(event.target===this)this.style.display='none'">
<div style="background:var(--ecg-card-bg);border:1px solid var(--ecg-border);
border-radius:8px;padding:28px 32px;min-width:280px;max-width:400px;
font-family:var(--font-mono);font-size:13px;color:var(--ecg-dark);">
<div style="font-family:var(--font-display);font-size:15px;font-weight:900;
color:var(--ecg-teal);margin-bottom:16px;letter-spacing:0.03em;">
Tastenkürzel
</div>
<table style="width:100%;border-collapse:collapse;line-height:1.9;">
<tr><td style="width:50px;"><kbd>j</kbd></td><td>Nächster Antrag</td></tr>
<tr><td><kbd>k</kbd></td><td>Vorheriger Antrag</td></tr>
<tr><td><kbd>Enter</kbd></td><td>Antrag öffnen</td></tr>
<tr><td><kbd>Esc</kbd></td><td>Detail schließen / zurück</td></tr>
<tr><td><kbd>/</kbd></td><td>Suche fokussieren</td></tr>
<tr><td><kbd>?</kbd></td><td>Diese Hilfe</td></tr>
</table>
<button onclick="document.getElementById('v2-kb-help-modal').style.display='none'"
style="margin-top:18px;font-family:var(--font-mono);font-size:11px;
background:none;border:1px solid var(--ecg-border);border-radius:4px;
padding:5px 14px;cursor:pointer;color:var(--ecg-dark);">
Schließen
</button>
</div>
</div>
<style>
.v2-kb-active {
outline: 2px solid var(--ecg-teal);
outline-offset: -2px;
border-radius: 4px;
}
kbd {
display: inline-block;
font-family: var(--font-mono);
font-size: 11px;
background: var(--ecg-border);
border: 1px solid color-mix(in srgb, var(--ecg-border) 60%, #000);
border-radius: 3px;
padding: 1px 5px;
color: var(--ecg-dark);
}
.gwoe-welcome-banner {
display: flex;
align-items: center;
gap: 14px;
flex-wrap: wrap;
margin-bottom: 14px;
padding: 14px 18px;
border-radius: 12px;
/* dezent-warmer Hint statt scharfes Outline-Kasten */
background: linear-gradient(
135deg,
color-mix(in srgb, var(--ecg-teal) 6%, var(--surface)) 0%,
color-mix(in srgb, var(--ecg-blue) 4%, var(--surface)) 100%
);
border: 1px solid color-mix(in srgb, var(--ecg-teal) 22%, transparent);
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04);
}
.gwoe-welcome-banner[hidden] { display: none !important; }
.gwoe-welcome-icon {
font-size: 22px;
flex-shrink: 0;
filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.08));
}
.gwoe-welcome-text {
flex: 1 1 320px;
font-size: 14px;
line-height: 1.55;
color: var(--ecg-dark);
}
.gwoe-welcome-text strong {
font-weight: 700;
color: var(--ecg-teal);
}
.gwoe-welcome-actions {
display: flex;
gap: 4px;
align-items: center;
}
.gwoe-welcome-skip {
background: none;
border: none;
color: var(--ecg-dark);
font-size: 12px;
font-family: var(--font-mono);
letter-spacing: 0.04em;
opacity: 0.55;
padding: 6px 10px;
cursor: pointer;
border-radius: 999px;
transition: opacity 0.15s ease, background 0.15s ease;
}
.gwoe-welcome-skip:hover {
opacity: 0.9;
background: color-mix(in srgb, var(--ecg-dark) 6%, transparent);
}
</style>
<script>
/* Welcome-Banner sichtbar bis Tour gestartet oder „Nein, danke".
localStorage: 'gwoe_welcome_dismissed' = '1' merkt den Schließ-Klick. */
(function () {
var KEY = 'gwoe_welcome_dismissed';
var banner = document.getElementById('gwoe-welcome-banner');
if (!banner) return;
if (localStorage.getItem(KEY) !== '1') {
banner.hidden = false;
}
window.gwoeWelcomeDismiss = function () {
try { localStorage.setItem(KEY, '1'); } catch (_) {}
banner.hidden = true;
};
// Wenn Tour gestartet wird, Banner auch wegblenden + dismissed merken.
var origStart = window.gwoeTourStart;
if (typeof origStart === 'function') {
window.gwoeTourStart = function () {
try { localStorage.setItem(KEY, '1'); } catch (_) {}
banner.hidden = true;
return origStart.apply(this, arguments);
};
}
})();
</script>
{% endblock %}