antragstracker/frontend/src/routes/anleitung/+page.svelte
Dotty Dotter f87ab389aa feat: Nutzungsanleitung + Legende auf /anleitung (#18)
- Neue Route /anleitung mit vollständiger Dokumentation
- 6 Abschnitte: Was ist der Tracker, Verfahrensstränge, Ampel-Legende,
  KI-Bewertung, konkrete Szenarien, Fristen-Tracking
- Lädt Strang-Definitionen von /api/ampel/definition
- Ampel-Beispiele inline mit Mock-Daten (inkl. Abzweigungen)
- Farbige Strang-Kästen, Inhaltsverzeichnis mit Sprungmarken
- Navigation in Desktop- und Mobile-Menü ergänzt
2026-04-02 23:09:44 +02:00

382 lines
16 KiB
Svelte
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.

<script lang="ts">
import { onMount } from 'svelte';
import Ampel from '$lib/components/Ampel.svelte';
import { fetchAmpelDefinition, type AmpelDefinition } from '$lib/api';
let definition = $state<AmpelDefinition | null>(null);
let error = $state<string | null>(null);
// Mock-Ampeldaten pro Strang für die Beispiele
const MOCK_AMPELN: Record<string, {
schritte: { id: string; label: string; aktiv: boolean; erreicht: boolean; farbe: string }[];
abzweigung: { id: string; label: string; farbe: string } | null;
kontrollfrage: string | null;
beschreibung: string;
ablauf: string;
}> = {
antrag: {
schritte: [
{ id: 'eingereicht', label: 'Eingereicht', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beraten', label: 'Beraten', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beschlossen', label: 'Beschlossen', aktiv: true, erreicht: true, farbe: 'gelb' },
{ id: 'umgesetzt', label: 'Umgesetzt', aktiv: false, erreicht: false, farbe: 'grau' },
],
abzweigung: null,
kontrollfrage: 'Hat die Verwaltung umgesetzt, was beschlossen wurde?',
beschreibung: 'Politik will etwas verändern — die Verwaltung soll es umsetzen.',
ablauf: 'Antrag wird eingereicht → in Ausschüssen beraten → vom Rat beschlossen → von der Verwaltung umgesetzt (oder auch nicht).',
},
anfrage: {
schritte: [
{ id: 'gestellt', label: 'Gestellt', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beantwortet', label: 'Beantwortet', aktiv: true, erreicht: true, farbe: 'gruen' },
],
abzweigung: null,
kontrollfrage: 'Wurde die Anfrage befriedigend beantwortet?',
beschreibung: 'Politik fragt — die Verwaltung antwortet.',
ablauf: 'Anfrage wird gestellt → Verwaltung antwortet schriftlich → KI bewertet, ob die Antwort die Frage tatsächlich beantwortet.',
},
beschlussvorlage: {
schritte: [
{ id: 'eingebracht', label: 'Eingebracht', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beraten', label: 'Beraten', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beschlossen', label: 'Beschlossen', aktiv: true, erreicht: true, farbe: 'gelb' },
{ id: 'umgesetzt', label: 'Umgesetzt', aktiv: false, erreicht: false, farbe: 'grau' },
],
abzweigung: null,
kontrollfrage: 'Wurde so umgesetzt wie beschlossen?',
beschreibung: 'Die Verwaltung legt einen Vorschlag vor — der Rat stimmt zu.',
ablauf: 'Verwaltung bringt Vorlage ein → Beratung in Ausschüssen → Ratsbeschluss → Umsetzung durch die Verwaltung.',
},
mitteilung: {
schritte: [
{ id: 'vorgelegt', label: 'Vorgelegt', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'kenntnisnahme', label: 'Kenntnisnahme', aktiv: true, erreicht: true, farbe: 'gruen' },
],
abzweigung: null,
kontrollfrage: null,
beschreibung: 'Die Verwaltung informiert den Rat — keine Abstimmung, nur Kenntnisnahme.',
ablauf: 'Verwaltung legt Mitteilung vor → Rat nimmt zur Kenntnis.',
},
};
// Beispiel-Ampel mit Abzweigung (versandet)
const MOCK_ABZWEIGUNG = {
strang: 'antrag',
strang_label: 'Antrag (versandet)',
kontrollfrage: 'Hat die Verwaltung umgesetzt?',
schritte: [
{ id: 'eingereicht', label: 'Eingereicht', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beraten', label: 'Beraten', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beschlossen', label: 'Beschlossen', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'umgesetzt', label: 'Umgesetzt', aktiv: false, erreicht: false, farbe: 'grau' },
],
abzweigung: { id: 'versandet', label: 'Versandet', farbe: 'rot' },
};
const MOCK_ABGELEHNT = {
strang: 'antrag',
strang_label: 'Antrag (abgelehnt)',
kontrollfrage: null,
schritte: [
{ id: 'eingereicht', label: 'Eingereicht', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beraten', label: 'Beraten', aktiv: false, erreicht: true, farbe: 'gruen' },
{ id: 'beschlossen', label: 'Beschlossen', aktiv: false, erreicht: false, farbe: 'grau' },
{ id: 'umgesetzt', label: 'Umgesetzt', aktiv: false, erreicht: false, farbe: 'grau' },
],
abzweigung: { id: 'abgelehnt', label: 'Abgelehnt', farbe: 'rot' },
};
const STRANG_FARBEN: Record<string, { bg: string; border: string; text: string }> = {
antrag: { bg: 'bg-green-50', border: 'border-green-200', text: 'text-green-800' },
anfrage: { bg: 'bg-blue-50', border: 'border-blue-200', text: 'text-blue-800' },
beschlussvorlage: { bg: 'bg-amber-50', border: 'border-amber-200', text: 'text-amber-800' },
mitteilung: { bg: 'bg-gray-50', border: 'border-gray-200', text: 'text-gray-700' },
};
const STRANG_ORDER = ['antrag', 'anfrage', 'beschlussvorlage', 'mitteilung'];
const TOC = [
{ id: 'was-ist', label: 'Was ist der Antragstracker?' },
{ id: 'straenge', label: 'Die vier Verfahrensstränge' },
{ id: 'ampel', label: 'Die Ampel verstehen' },
{ id: 'ki-bewertung', label: 'Die KI-Bewertung' },
{ id: 'szenarien', label: 'Konkrete Szenarien' },
{ id: 'fristen', label: 'Fristen-Tracking' },
];
onMount(async () => {
try {
definition = await fetchAmpelDefinition();
} catch (e) {
error = 'Strang-Definitionen konnten nicht geladen werden.';
console.error(e);
}
});
function strangLabel(key: string): string {
if (definition?.straenge[key]) return definition.straenge[key].label;
const fallback: Record<string, string> = {
antrag: 'Anträge',
anfrage: 'Anfragen',
beschlussvorlage: 'Beschlussvorlagen',
mitteilung: 'Mitteilungen',
};
return fallback[key] || key;
}
function buildMockAmpel(key: string) {
const mock = MOCK_AMPELN[key];
if (!mock) return null;
return {
strang: key,
strang_label: strangLabel(key),
kontrollfrage: mock.kontrollfrage,
schritte: mock.schritte,
abzweigung: mock.abzweigung,
};
}
</script>
<svelte:head>
<title>Anleitung — Antragstracker Hagen</title>
</svelte:head>
<div class="max-w-3xl mx-auto">
<h1 class="text-3xl font-bold text-gray-900 mb-2">Anleitung</h1>
<p class="text-gray-500 mb-8">Wie der Antragstracker funktioniert — und wie du ihn nutzt.</p>
<!-- Inhaltsverzeichnis -->
<nav class="bg-white rounded-lg border border-gray-200 p-4 mb-10">
<p class="text-xs font-semibold text-gray-400 uppercase tracking-wide mb-2">Inhalt</p>
<ol class="space-y-1">
{#each TOC as item}
<li>
<a href="#{item.id}" class="text-sm text-green-700 hover:text-green-900 hover:underline">
{item.label}
</a>
</li>
{/each}
</ol>
</nav>
<!-- 1. Was ist der Antragstracker? -->
<section id="was-ist" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Was ist der Antragstracker?</h2>
<div class="prose prose-gray max-w-none">
<p>
Der Antragstracker ist ein kommunalpolitisches Kontroll-Instrument für den Rat der Stadt Hagen.
Er verfolgt den Weg von Anträgen, Anfragen und Beschlüssen durch die Gremien — und macht sichtbar,
was beschlossen wurde und was davon tatsächlich umgesetzt wird.
</p>
<p>
Daten werden aus dem Ratsinformationssystem (Allris) importiert. Eine KI analysiert den aktuellen
Stand jedes Vorgangs und bewertet, ob beschlossene Maßnahmen umgesetzt wurden.
</p>
</div>
</section>
<!-- 2. Die vier Verfahrensstränge -->
<section id="straenge" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Die vier Verfahrensstränge</h2>
<p class="text-gray-600 mb-6">
Jeder Vorgang im Rat folgt einem von vier Verfahrenssträngen. Jeder Strang hat eigene Schritte,
eine eigene Ampel — und eine eigene Kontrollfrage.
</p>
{#if error}
<div class="bg-red-50 border border-red-200 rounded-lg p-3 text-sm text-red-700 mb-4">{error}</div>
{/if}
<div class="space-y-6">
{#each STRANG_ORDER as key}
{@const farben = STRANG_FARBEN[key]}
{@const mock = MOCK_AMPELN[key]}
{@const ampelData = buildMockAmpel(key)}
<div class="{farben.bg} border {farben.border} rounded-lg p-5">
<h3 class="text-lg font-semibold {farben.text} mb-1">{strangLabel(key)}</h3>
<p class="text-sm text-gray-700 mb-3">{mock.beschreibung}</p>
{#if ampelData}
<div class="bg-white/60 rounded-md p-3 mb-3">
<Ampel ampel={ampelData} />
</div>
{/if}
{#if mock.kontrollfrage}
<p class="text-sm font-medium text-gray-800 mb-1">
Kontrollfrage: <span class="italic">{mock.kontrollfrage}</span>
</p>
{/if}
<p class="text-sm text-gray-600">
<span class="font-medium">Ablauf:</span> {mock.ablauf}
</p>
</div>
{/each}
</div>
</section>
<!-- 3. Die Ampel verstehen -->
<section id="ampel" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Die Ampel verstehen</h2>
<div class="prose prose-gray max-w-none mb-6">
<p>
Jeder Vorgang hat eine Ampel, die seinen Fortschritt zeigt. Die Schritte werden
von links nach rechts durchlaufen.
</p>
</div>
<div class="space-y-4 mb-6">
<div class="flex items-start gap-3">
<div class="w-5 h-5 rounded-full border-2 border-gray-300 bg-white shrink-0 mt-0.5"></div>
<div>
<p class="text-sm font-medium text-gray-900">Offen (weißer Kreis)</p>
<p class="text-sm text-gray-600">Dieser Schritt wurde noch nicht erreicht.</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-5 h-5 rounded-full bg-gray-300 shrink-0 mt-0.5"></div>
<div>
<p class="text-sm font-medium text-gray-900">Durchlaufen (grau)</p>
<p class="text-sm text-gray-600">Dieser Schritt liegt in der Vergangenheit — er wurde bereits passiert.</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-5 h-5 rounded-full bg-yellow-500 shrink-0 mt-0.5" style="box-shadow: 0 0 0 3px rgba(234, 179, 8, 0.19)"></div>
<div>
<p class="text-sm font-medium text-gray-900">Aktuell — wartet (gelb)</p>
<p class="text-sm text-gray-600">Der Vorgang befindet sich gerade in diesem Schritt. Es passiert etwas — oder es wird darauf gewartet.</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-5 h-5 rounded-full bg-green-500 shrink-0 mt-0.5" style="box-shadow: 0 0 0 3px rgba(34, 197, 94, 0.19)"></div>
<div>
<p class="text-sm font-medium text-gray-900">Erledigt (grün)</p>
<p class="text-sm text-gray-600">Dieser Schritt ist abgeschlossen — der aktive Schritt leuchtet grün, wenn er positiv abgeschlossen ist.</p>
</div>
</div>
<div class="flex items-start gap-3">
<div class="w-5 h-5 rounded-full bg-red-500 shrink-0 mt-0.5" style="box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.19)"></div>
<div>
<p class="text-sm font-medium text-gray-900">Gescheitert (rot)</p>
<p class="text-sm text-gray-600">Hier ist etwas schiefgegangen — abgelehnt, gescheitert oder nicht umgesetzt.</p>
</div>
</div>
</div>
<h3 class="text-lg font-semibold text-gray-900 mb-3">Abzweigungen</h3>
<p class="text-sm text-gray-600 mb-4">
Manchmal nimmt ein Vorgang nicht den normalen Weg. In diesen Fällen zeigt die Ampel eine Abzweigung:
</p>
<div class="space-y-4">
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-medium text-red-700 mb-2">Versandet</p>
<p class="text-sm text-gray-600 mb-3">
Ein Antrag wurde beschlossen, aber nie umgesetzt. Die Verwaltung hat ihn schlicht nicht weiterverfolgt.
</p>
<Ampel ampel={MOCK_ABZWEIGUNG} />
</div>
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-medium text-red-700 mb-2">Abgelehnt</p>
<p class="text-sm text-gray-600 mb-3">
Der Vorgang wurde in der Beratung oder Abstimmung abgelehnt und nicht weiterverfolgt.
</p>
<Ampel ampel={MOCK_ABGELEHNT} />
</div>
</div>
</section>
<!-- 4. Die KI-Bewertung -->
<section id="ki-bewertung" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Die KI-Bewertung</h2>
<div class="prose prose-gray max-w-none">
<p>
Jeder Vorgang wird von einer KI analysiert. Sie liest die zugehörigen Dokumente aus dem
Ratsinformationssystem und erstellt:
</p>
<ul>
<li><strong>Eine Zusammenfassung</strong> — worum geht es, was wurde beschlossen?</li>
<li><strong>Einen Umsetzungs-Score</strong> — wie weit ist die Umsetzung fortgeschritten (0100%)?</li>
</ul>
<h3>Was bedeutet der Prozentwert?</h3>
<ul>
<li><strong>0%</strong> — Keine erkennbare Umsetzung</li>
<li><strong>149%</strong> — Teilweise umgesetzt, wesentliche Teile fehlen</li>
<li><strong>5079%</strong> — Überwiegend umgesetzt, aber nicht vollständig</li>
<li><strong>80100%</strong> — Weitgehend bis vollständig umgesetzt</li>
</ul>
<h3>Neubewertung</h3>
<p>
Bewertungen können neu angefordert werden — etwa wenn neue Informationen vorliegen oder eine
Einschätzung fragwürdig erscheint. Dabei kann eine Anmerkung mitgegeben werden, die der KI
als zusätzlichen Kontext dient.
</p>
<h3>Versionierung</h3>
<p>
Jede Bewertung wird versioniert. Alte Bewertungen bleiben erhalten und sind nachvollziehbar.
So lässt sich verfolgen, wie sich die Einschätzung über die Zeit verändert hat.
</p>
</div>
</section>
<!-- 5. Konkrete Szenarien -->
<section id="szenarien" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Konkrete Szenarien</h2>
<div class="space-y-3">
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-semibold text-gray-900 mb-1">„Welche beschlossenen Anträge wurden nie umgesetzt?"</p>
<p class="text-sm text-gray-600">
<a href="/explorer" class="text-green-700 hover:underline">Explorer</a> öffnen, nach Status „versandet" filtern.
Zeigt alle Vorgänge, bei denen die Verwaltung beschlossene Maßnahmen nicht weiterverfolgt hat.
</p>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-semibold text-gray-900 mb-1">„Hat die Verwaltung meine Anfrage wirklich beantwortet?"</p>
<p class="text-sm text-gray-600">
→ Anfrage im Explorer suchen, KI-Bewertung lesen. Falls unbefriedigend: Neubewertung mit
eigener Anmerkung anfordern.
</p>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-semibold text-gray-900 mb-1">„Welche Fristen laufen demnächst ab?"</p>
<p class="text-sm text-gray-600">
<a href="/fristen" class="text-green-700 hover:underline">Fristen</a> öffnen.
Zeigt alle anstehenden und überfälligen Termine aus Beschlusstexten.
</p>
</div>
<div class="bg-white border border-gray-200 rounded-lg p-4">
<p class="text-sm font-semibold text-gray-900 mb-1">„Wie stimmt meine Fraktion im Vergleich zu anderen ab?"</p>
<p class="text-sm text-gray-600">
<a href="/abstimmungen" class="text-green-700 hover:underline">Abstimmungen</a> öffnen.
Vergleicht das Abstimmungsverhalten aller Fraktionen.
</p>
</div>
</div>
</section>
<!-- 6. Fristen-Tracking -->
<section id="fristen" class="mb-12 scroll-mt-24">
<h2 class="text-2xl font-bold text-gray-900 mb-4">Fristen-Tracking</h2>
<div class="prose prose-gray max-w-none">
<p>
In vielen Beschlüssen stecken konkrete Termine: „bis Ende Q2 umsetzen", „Bericht bis März vorlegen".
Der Antragstracker erkennt solche Fristen automatisch per KI und macht sie sichtbar.
</p>
<ul>
<li><strong>Automatische Erkennung</strong> — Die KI extrahiert Termine aus Beschlusstexten</li>
<li><strong>Manuelle Ergänzung</strong> — Fristen können auch von Hand hinzugefügt werden</li>
<li><strong>Farbkodierung</strong> — Überfällige Fristen werden rot markiert, anstehende gelb</li>
</ul>
<p>
Alle Fristen sind unter <a href="/fristen">/fristen</a> einsehbar, gefiltert nach Status und Zeitraum.
</p>
</div>
</section>
</div>