gwoe-antragspruefer/app/templates/v2/screens/neu.html

230 lines
7.2 KiB
HTML
Raw Normal View History

{% extends "v2/base.html" %}
{% block title %}Neuer Antrag — GWÖ-Antragsprüfer{% endblock %}
{% set v2_active_nav = "neu" %}
{% block head_extra %}
<style>
.neu-form {
max-width: 560px;
}
.neu-form label {
display: block;
font-family: var(--font-mono);
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.06em;
opacity: 0.7;
margin-bottom: 4px;
}
.neu-form input[type="text"],
.neu-form select {
width: 100%;
font-family: var(--font-mono);
font-size: 13px;
padding: 8px 10px;
border: 1px solid var(--ecg-border);
border-radius: 4px;
background: var(--ecg-card-bg);
color: var(--ecg-dark);
margin-bottom: 14px;
}
.neu-form input[type="text"]:focus,
.neu-form select:focus {
outline: none;
border-color: var(--ecg-teal);
}
.neu-submit {
font-family: var(--font-display);
font-size: 13px;
font-weight: 700;
padding: 10px 24px;
background: var(--ecg-teal);
color: #fff;
border: none;
border-radius: 4px;
cursor: pointer;
letter-spacing: 0.04em;
transition: opacity 0.1s;
}
.neu-submit:hover { opacity: 0.85; }
.neu-submit:disabled { opacity: 0.45; cursor: not-allowed; }
#neu-status {
margin-top: 1rem;
font-family: var(--font-mono);
font-size: 12px;
display: none;
}
.neu-progress {
display: flex;
align-items: center;
gap: 8px;
color: var(--ecg-teal);
}
.neu-spinner {
width: 14px; height: 14px;
border: 2px solid var(--ecg-teal);
border-top-color: transparent;
border-radius: 50%;
animation: spin 0.7s linear infinite;
flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }
.neu-error { color: #c00; }
</style>
{% endblock %}
{% block main %}
<div style="padding:0 0 1.5rem;">
<h1 style="font-family:var(--font-display);font-size:22px;color:var(--ecg-teal);margin:0 0 4px;">Neuer Antrag prüfen</h1>
<p style="font-size:12px;font-family:var(--font-mono);color:var(--ecg-dark);opacity:0.6;">
Drucksachen-Nr. eingeben · Analyse startet sofort
</p>
</div>
<div class="v2-kasten outline-blue" style="max-width:560px;margin-bottom:1.5rem;">
<p style="font-size:12px;">
Der Prüfer holt den Antragstext aus dem Landtags-Portal, bewertet ihn nach der GWÖ-Matrix 2.0
und zeigt Wahlprogramm-Treue sowie Verbesserungsvorschläge. Die Analyse dauert 3090 Sekunden.
</p>
</div>
<form class="neu-form" onsubmit="startAnalyse(event)">
<div id="neu-bl-hint" style="display:none;margin-bottom:14px;padding:8px 12px;background:color-mix(in srgb,var(--ecg-teal) 10%,transparent);border:1px solid var(--ecg-teal);border-radius:4px;font-family:var(--font-mono);font-size:11px;color:var(--ecg-teal);">
Bitte zuerst ein Bundesland im Header wählen.
</div>
<label for="neu-drucksache">Drucksachen-Nummer</label>
<input type="text" id="neu-drucksache" name="drucksache"
placeholder="z. B. 18/12345 oder NRW-18/12345"
required autocomplete="off">
<label for="neu-model">Modell</label>
<select id="neu-model" name="model">
<option value="">Standard ({{ default_model }})</option>
<option value="qwen-plus">qwen-plus</option>
<option value="qwen-max">qwen-max</option>
</select>
<button type="submit" class="neu-submit" id="neu-btn">Analyse starten</button>
</form>
<div id="neu-status">
<div id="neu-progress" class="neu-progress" style="display:none;">
<div class="neu-spinner"></div>
<span id="neu-progress-text">Analyse läuft …</span>
</div>
<div id="neu-error" class="neu-error" style="display:none;"></div>
</div>
{% endblock %}
{% block body_scripts %}
<script>
async function startAnalyse(e) {
e.preventDefault();
const btn = document.getElementById('neu-btn');
const statusEl = document.getElementById('neu-status');
const progEl = document.getElementById('neu-progress');
const progText = document.getElementById('neu-progress-text');
const errEl = document.getElementById('neu-error');
const drucksache = document.getElementById('neu-drucksache').value.trim();
const bundesland = (window.v2GetGlobalBl && window.v2GetGlobalBl()) || 'ALL';
const model = document.getElementById('neu-model').value;
if (!drucksache) return;
if (bundesland === 'ALL') {
errEl.style.display = '';
errEl.textContent = 'Bitte zuerst ein Bundesland im Header wählen.';
return;
}
btn.disabled = true;
statusEl.style.display = '';
progEl.style.display = '';
errEl.style.display = 'none';
progText.textContent = 'Analyse läuft … (3090 s)';
try {
const fd = new FormData();
fd.append('drucksache', drucksache);
fd.append('bundesland', bundesland);
if (model) fd.append('model', model);
const resp = await fetch('/api/analyze-drucksache', { method: 'POST', body: fd });
if (resp.status === 401 || resp.status === 403) {
progEl.style.display = 'none';
errEl.style.display = '';
errEl.textContent = 'Sitzung abgelaufen — bitte erneut anmelden.';
btn.disabled = false;
if (typeof window.v2AuthModalOpen === 'function') window.v2AuthModalOpen();
return;
}
if (!resp.ok) {
const err = await resp.json().catch(() => ({ detail: resp.statusText }));
throw new Error(err.detail || ('HTTP ' + resp.status));
}
const data = await resp.json();
const ds = data.drucksache || drucksache;
if (data.status === 'already_checked') {
progText.textContent = 'Bereits bewertet. Weiterleitung …';
setTimeout(() => { window.location.href = '/antrag/' + encodeURIComponent(ds); }, 400);
return;
}
if (data.status === 'skipped') {
progEl.style.display = 'none';
errEl.style.display = '';
errEl.textContent = 'Antrag-Typ "' + (data.typ || 'unbekannt') + '" ist nicht abstimmbar — keine GWÖ-Bewertung sinnvoll.';
btn.disabled = false;
return;
}
// Job-Polling bis Abschluss, dann redirect
const jobId = data.job_id;
if (!jobId) { window.location.href = '/antrag/' + encodeURIComponent(ds); return; }
let attempts = 0;
const maxAttempts = 90;
while (attempts < maxAttempts) {
await new Promise(r => setTimeout(r, 2000));
attempts++;
const st = await fetch('/status/' + jobId).then(r => r.json()).catch(() => null);
if (!st) continue;
progText.textContent = 'Analyse läuft … (' + (st.status || '?') + ', ~' + (attempts * 2) + 's)';
if (st.status === 'completed') {
progText.textContent = 'Fertig. Weiterleitung …';
setTimeout(() => { window.location.href = '/antrag/' + encodeURIComponent(ds); }, 400);
return;
}
if (st.status === 'failed' || st.status === 'rejected') {
throw new Error('Analyse fehlgeschlagen: ' + (st.error || 'unbekannt'));
}
}
throw new Error('Analyse-Timeout (>3 min)');
} catch (err) {
progEl.style.display = 'none';
errEl.style.display = '';
errEl.textContent = 'Fehler: ' + err.message;
btn.disabled = false;
}
}
document.addEventListener('DOMContentLoaded', function () {
var hint = document.getElementById('neu-bl-hint');
function updateHint() {
var bl = (window.v2GetGlobalBl && window.v2GetGlobalBl()) || 'ALL';
if (hint) hint.style.display = (bl === 'ALL') ? '' : 'none';
}
updateHint();
window.addEventListener('v2-bl-changed', updateHint);
});
</script>
{% endblock %}