feat: Fragebogen mit adaptiver Logik (Schritt 2 der Baureihenfolge)

Ebene 2 ("Antrag stellen") als einseitiges Formular statt mehrseitigem
Assistenten — jede zusätzliche Seite kostet Zeit, und laut Spezifikation
wird ein Antrag umgangen, wenn er länger als fünf Minuten dauert.

- GET /antraege/neu: Fragebogen-Formular (Abschnitte A-D exakt nach
  Spezifikation). Adaptive Folgefragen (C2: welche Art von Entscheidung,
  C3: welche Art von Erkennung) werden rein per CSS :has() ein-/
  ausgeblendet, kein JavaScript nötig — visuell mit Chromium-Screenshots
  verifiziert (unchecked vs. checked).
- POST /antraege: legt an und reicht direkt ein (entwurf->eingereicht in
  einem Schritt, kein Zwischenspeichern als Entwurf für v1). Die
  antworten-JSON nutzt exakt dieselben Fakten-Schlüssel wie
  rules/*.yaml (b1-b7, c1-c5, c2_folge, c3_art) — Schritt 3 kann sie
  direkt auswerten, ohne Felder umzubenennen. "Unsicher" wird bewusst
  NICHT zu "ja" normalisiert (das ist eine Auswertungsregel für Schritt
  3, keine Speicherregel) — der Antrag hält fest, was der Mitarbeiter
  tatsächlich geantwortet hat.
- GET /antraege: eigene Anträge (Ebene 2 sieht nur eigene, nicht die
  des ganzen Mandanten — dafür neue Store-Methode
  ListAntraegeForUser, getrennt von ListAntraegeForAccount für den
  späteren Ebene-3-Posteingang).
- GET /antraege/{id}: Detail, fremder Antrag liefert 404 (nicht 403,
  gleiches Muster wie überall sonst im Projekt).
- betreiber-Rolle wird von allen Antrags-Routen weggeleitet (Ebene 5
  ist technisch getrennt vom Mandantenbereich).

rules/OPEN.md Punkt 3 (fehlende Fragebogen-Unterscheidung für die
"verboten"-Varianten) damit geklärt: c3_art existiert jetzt mit den
bereits in rules/kivo_einstufung.yaml erwarteten Werten.

Volle Testsuite inkl. echter Postgres-Tests grün; End-to-End gegen
einen laufenden Server verifiziert (Antrag anlegen, antworten-JSON in
der DB korrekt, Detail-/Listenansicht, Mandantentrennung).
This commit is contained in:
noroot
2026-08-28 23:09:24 +02:00
parent 344a33805a
commit db9c5b7482
14 changed files with 719 additions and 25 deletions

View File

@@ -0,0 +1,26 @@
{{define "antraege-liste"}}<!doctype html>
<html lang="de">
<head>{{template "head" .}}</head>
<body>
{{template "nav" .Nav}}
<div class="page">
<h1>Meine Anträge</h1>
{{if not .Antraege}}
<p class="hinweis">Noch keine Anträge gestellt.</p>
{{else}}
<ul class="beitraege-liste">
{{range .Antraege}}
<li>
<a href="/antraege/{{.ID}}">
<strong>{{.Titel}}</strong> · {{.Haeufigkeit}} · {{.CreatedAt}}
<span class="status status-{{.Status}}">{{.Status}}</span>
</a>
</li>
{{end}}
</ul>
{{end}}
<p><a href="/antraege/neu">Neuen Antrag stellen</a></p>
</div>
</body>
</html>
{{end}}

View File

@@ -0,0 +1,24 @@
{{define "antrag-detail"}}<!doctype html>
<html lang="de">
<head>{{template "head" .}}</head>
<body>
{{template "nav" .Nav}}
<div class="page">
<p><a href="/antraege">&larr; Meine Anträge</a></p>
<h1>{{.Titel}}</h1>
<p class="hinweis">Eingereicht am {{.CreatedAt}} — Status: <span class="status status-{{.Status}}">{{.Status}}</span></p>
<h2>Vorhaben</h2>
<p><strong>Was soll die KI tun?</strong><br>{{.Beschreibung}}</p>
<p><strong>Was soll herauskommen?</strong><br>{{.Ergebnis}}</p>
<p><strong>Häufigkeit:</strong> {{.Haeufigkeit}}</p>
<p class="hinweis">
Die Bewertung (Datenklasse, KI-VO-Einstufung, Werkzeugvorschlag) folgt
in einer späteren Ausbaustufe — dieser Antrag ist gespeichert und
eingereicht.
</p>
</div>
</body>
</html>
{{end}}

View File

@@ -0,0 +1,122 @@
{{define "antrag-neu"}}<!doctype html>
<html lang="de">
<head>{{template "head" .}}</head>
<body>
{{template "nav" .Nav}}
<div class="page">
<h1>Antrag stellen</h1>
{{if .Error}}<p class="fehler">{{.Error}}</p>{{end}}
<form method="post" action="/antraege" class="fragebogen">
<h2>Vorhaben</h2>
<label for="titel">Kurzbezeichnung</label>
<input type="text" id="titel" name="titel" required>
<label for="beschreibung">Was soll die KI tun?</label>
<textarea id="beschreibung" name="beschreibung" rows="3" required></textarea>
<label for="ergebnis">Was soll herauskommen?</label>
<textarea id="ergebnis" name="ergebnis" rows="2" required></textarea>
<label for="haeufigkeit">Häufigkeit</label>
<select id="haeufigkeit" name="haeufigkeit" required>
<option value="einmalig">einmalig</option>
<option value="gelegentlich">gelegentlich</option>
<option value="taeglich">täglich</option>
<option value="automatisiert">automatisiert</option>
</select>
{{if .Abteilungen}}
<label for="abteilung_id">Abteilung</label>
<select id="abteilung_id" name="abteilung_id">
<option value="">— keine Angabe —</option>
{{range .Abteilungen}}
<option value="{{.ID}}">{{.Name}}</option>
{{end}}
</select>
{{end}}
<h2>Daten</h2>
<p class="hinweis">Bei Unsicherheit "Unsicher" wählen — das wird wie "Ja" behandelt.</p>
{{range .Datenfragen}}
<fieldset class="datenfrage">
<legend>{{.Label}}</legend>
<label><input type="radio" name="{{.Key}}" value="ja" required> Ja</label>
<label><input type="radio" name="{{.Key}}" value="nein"> Nein</label>
<label><input type="radio" name="{{.Key}}" value="unsicher"> Unsicher</label>
</fieldset>
{{end}}
<h2>Verwendung des Ergebnisses</h2>
<fieldset>
<legend>Wird das Ergebnis unverändert nach außen gegeben?</legend>
<label><input type="radio" name="c1" value="ja" required> Ja</label>
<label><input type="radio" name="c1" value="nein"> Nein</label>
</fieldset>
<fieldset class="mit-folgefrage">
<legend>Beeinflusst es eine Entscheidung über einen Menschen?</legend>
<label class="c2-ja"><input type="radio" name="c2" value="ja" required> Ja</label>
<label><input type="radio" name="c2" value="nein"> Nein</label>
<div class="folgefrage">
<label for="c2_folge">Welche Art von Entscheidung?</label>
<select id="c2_folge" name="c2_folge">
<option value="beschaeftigung">Einstellung, Beförderung, Kündigung</option>
<option value="kreditwuerdigkeit">Kreditwürdigkeit</option>
<option value="leistungsbewilligung">Leistungsbewilligung</option>
<option value="bildung">Zugang zu Bildung</option>
<option value="gesundheit">medizinische Beurteilung</option>
<option value="keine">keins davon</option>
</select>
</div>
</fieldset>
<fieldset class="mit-folgefrage">
<legend>Erkennt oder bewertet es Emotionen, Verhalten oder biometrische Merkmale?</legend>
<label class="c3-ja"><input type="radio" name="c3" value="ja" required> Ja</label>
<label><input type="radio" name="c3" value="nein"> Nein</label>
<div class="folgefrage">
<label for="c3_art">Welche Art?</label>
<select id="c3_art" name="c3_art">
<option value="emotionserkennung_arbeitsplatz">Emotionserkennung am Arbeitsplatz</option>
<option value="social_scoring">Social Scoring</option>
<option value="biometrische_kategorisierung">Biometrische Kategorisierung</option>
<option value="keine">etwas anderes</option>
</select>
</div>
</fieldset>
<fieldset>
<legend>Läuft es ohne dass ein Mensch das Ergebnis prüft?</legend>
<label><input type="radio" name="c4" value="ja" required> Ja</label>
<label><input type="radio" name="c4" value="nein"> Nein</label>
</fieldset>
<fieldset>
<legend>Merkt der Empfänger, dass ein Ergebnis von einer KI stammt?</legend>
<label><input type="radio" name="c5" value="ja" required> Ja</label>
<label><input type="radio" name="c5" value="nein"> Nein</label>
</fieldset>
<h2>Werkzeug</h2>
<label for="d_werkzeug_freitext">Haben Sie schon ein Werkzeug im Sinn? (optional)</label>
<input type="text" id="d_werkzeug_freitext" name="d_werkzeug_freitext">
<fieldset>
<legend>Firmenkonto oder privater Zugang?</legend>
<label><input type="radio" name="d_zugang" value="firma" required> Firmenkonto</label>
<label><input type="radio" name="d_zugang" value="privat"> Privater Zugang</label>
</fieldset>
<fieldset>
<legend>Auf Firmengerät oder Privatgerät?</legend>
<label><input type="radio" name="d_geraet" value="firma" required> Firmengerät</label>
<label><input type="radio" name="d_geraet" value="privat"> Privatgerät</label>
</fieldset>
<button type="submit">Antrag einreichen</button>
</form>
</div>
</body>
</html>
{{end}}

View File

@@ -5,10 +5,10 @@
{{template "nav" .Nav}}
<div class="page">
<h1>Willkommen bei Deklarix</h1>
<p class="hinweis">
Der geführte Fragebogen für die Antragsprüfung folgt in der nächsten
Ausbaustufe. Datenmodell, Regelwerk und Werkzeugkatalog stehen bereits.
</p>
<ul class="admin-kacheln">
<li><a href="/antraege/neu">Antrag stellen</a></li>
<li><a href="/antraege">Meine Anträge</a></li>
</ul>
</div>
</body>
</html>

View File

@@ -9,6 +9,7 @@
{{define "nav"}}
<nav>
<a href="/">Start</a>
{{if not .IsBetreiber}}<a href="/antraege">Meine Anträge</a>{{end}}
{{if .IsBetreiber}}<a href="/betreiber">Plattform</a>{{end}}
<form method="post" action="/logout" style="display:inline">
<button type="submit">Abmelden</button>